Nested clusters of content

Introducing nested clusters of content to our layout can break the content up a bit and disturb the flow a little which is our intention. It can be used to give the effect that content is spanning columns.

Nested clusters of contents are essentially nested layouts that will either flow vertically or horizontally. Flexbox is a great fit here.

A vertically flowing 2 column nested cluster

Our base layout doesn’t fit perfectly into a quadrangle but our clusters must. This is because we don’t want patches of white space appearing in our masonry layout. We could define cluster size classes to combat this. Consider the following example sizes;

.masonry-layout-panel--small {

height: 200px;

}

.masonry-layout-panel--medium {

height: 300px;

}

.masonry-layout-panel--large {

height: 400px;

}

But, this doesn’t quite feel right. Set sizes aren’t going to play nice in every scenario. The better approach may be to introduce the column-like containers we discussed earlier. Consider something like the following;

<div class=”masonry-layout-panel masonry-layout-cluster masonry-layout-cluster--vertical”>

<div class=”masonry-layout-cluster__segment masonry-layout-cluster__segment--column”>

<div class=”masonry-layout-panel masonry-layout-cluster__segment”>

<div class=”masonry-layout-panel__content”>

<h1>hello</h1>

</div>

</div>

</div>

<div class=”masonry-layout-cluster__segment masonry-layout-cluster__segment--column”>

<div class=”masonry-layout-panel masonry-layout-cluster__segment”>

<div class=”masonry-layout-panel__content”>

<h1>yes</h1>

</div>

</div>

<div class=”masonry-layout-panel masonry-layout-cluster__segment”>

<div class=”masonry-layout-panel__content”>

<p>

Some cool pics.

</p>

</div>

</div>

</div>

</div>

Clusters can either flow horizontally or vertically. If a cluster flows horizontally, we define columns of segments that flow vertically and vice-versa. We alter the flow using “flex-direction”. Cluster segments grow/shrink to accommodate white space with clusters becoming blocks in themselves.

If we want some tighter control on the cluster segments, we can use utility classes to define percentage flex-basis for them. We don’t need a “full-size” utility class as we can just include one item in a container and it will span full width or height. For say “half-size” though we could define 50% flex-basis. The nice feature of this implementation is that any number of rows or columns should span accordingly to the content.

How might the code look? Consider the following;

.masonry-layout-cluster {

display: flex;

padding: 0;

}

.masonry-layout-cluster--vertical {

flex-direction: row;

}

.masonry-layout-cluster--horizontal {

flex-direction: column;

}

.masonry-layout-cluster__segment {

display: flex;

flex: 1 1 auto;

}

.masonry-layout-cluster__segment--row {

flex-direction: row;

}

.masonry-layout-cluster__segment--column {

flex-direction: column;

}

.masonry-layout-cluster__segment--half {

flex-basis: 50%;

}

.masonry-layout-cluster__segment--quarter {

flex-basis: 25%;

}

You can see the code in action here. Nested clusters are the trickiest concept to explain, and with them out of the way, we can tackle some easier features!

Theming & image panels

The aim is to keep our layout structure styling generic and isolated from theming styles. This promotes easier re-use in other projects. If you’ve checked out any of the code demos mentioned previously, you’ll notice that content panels have rounded borders etc. This can be achieved by adding some minimal theming.

A picture panel

.masonry-layout-panel__content {

border-radius: 10px;

overflow: hidden;

padding: 10px;

}

How about images? It’s quite common that you may wish to display images as complete panels in your masonry layout. If we put an image within our panel content it would be affected by the padding we’ve defined for our theme previously. So why don’t we just give the “masonry-layout__panel-content” class to our image with a modifier?

<img src="img/photo.jpg" class="masonry-layout-panel__content--img">

It’s nearly there but not quite. If we add a little theming specific to images we can get round this.

.masonry-layout-panel__content--img {

max-width: 100%;

padding: 0;

}

Check out how it’s starting to look here. Note: Because the classnames are becoming rather bloated, from here on, abbreviations are being used in code examples. For example; “masonry-layout” becomes “ml”, “masonry-layout-cluster” becomes “ml-clstr” etc. Also, image placeholders display in yellow.

Animating panels on hover

Panel mid-flip

How about making panels respond to interaction? We could make it so that panels flip and reveal extra content when we hover over them. In order to do this we make our panel content a flip card. Adding a front and back content container we can define the front and back for a panel. This does requires that we have a set height for our panels so we can use utility classes for defined heights. For example;

.masonry-layout-flip--medium {

height: 300px;

}

We introduce a new block for flippers.

THE MARKUP <div class=”masonry-layout-panel masonry-layout-flip--medium masonry-layout-flip”>

<div class=”masonry-layout-panel__content masonry-layout-flip__content”>

<img src=”img/photo-1.jpg” class=”masonry-layout-flip__panel masonry-layout-flip__panel--front masonry-layout-flip__panel--img”/>

<div class=”masonry-layout-flip__panel masonry-layout-flip__panel--back”>

<p>Here is a flpped image…</p>

</div>

</div>

</div> /* NOTE: long classnames can be avoided in real projects by using abbreviations such as "ml-flp" */

Using CSS3 transforms and transitions we can apply a hover animation so that our panel flips and shows a reverse side to it.

/* CSS FOR FLIPPER PANELS */ .masonry-layout-flip {

perspective: 1000;

}

.masonry-layout-flip:hover .masonry-layout-flip__content {

transform: rotateY(180deg);

}

.masonry-layout-flip--md {

height: 300px;

}

.masonry-layout-flip__panel {

backface-visibility: hidden;

border-radius: 10px;

height: 100%;

left: 0;

overflow: hidden;

position: absolute;

top: 0;

width: 100%;

}

.masonry-layout-flip__panel--front {

transform: rotateY(0deg);

z-index: 2;

}

.masonry-layout-flip__panel--back {

transform: rotateY(180deg);

}

.masonry-layout-flip__content {

height: 100%;

overflow: visible;

position: relative;

transform-style: preserve-3d;

transition: 0.25s;

}

The layout is taking shape, see the animated panel in action here.

Panel with focus effect

We aren’t just limited to flip panels. We could add various effects and features to panels such as focus effects. I’ll include some bonus features in the final bonus code.

Going responsive

If you’ve got this far, you’ll notice that our masonry layout isn’t looking great on smaller viewports. Let’s make it responsive. How do we want it to respond? As our viewport increases in size, we want to increase the amount of columns in our layout. As our viewport decreases in size, in addition the amount of columns decreasing, we might want our nested clusters to collapse and allow the segments to take up full width of the layout. We will also be looking to develop our layout styles mobile first. Consider the following for responsive column count;

For nested clusters, we want to ignore things such as horizontal flow and flex-basis at lower viewports. We want our clusters to collapse and segments take up full viewport width regardless of whether they are columns, rows, etc.

/* CLUSTER FLOW IGNORED AT LOWER VIEWPORT */

.masonry-layout-cluster__segment--row {

flex-direction: row;

}

}

(min-width: 768px) {

.masonry-layout-cluster--vertical {

flex-direction: row;

}

} @media (min-width: 768px) {.masonry-layout-cluster__segment--row {flex-direction: row; @media (min-width: 768px) {.masonry-layout-cluster--vertical {flex-direction: row;

/* FLEX-BASIS IGNORED AT LOWER VIEWPORT */

.masonry-layout-cluster__segment--half {

flex-basis: 50%;

}

.masonry-layout-cluster__segment--quarter {

flex-basis: 25%;

}

} @media (min-width: 768px) {.masonry-layout-cluster__segment--half {flex-basis: 50%;.masonry-layout-cluster__segment--quarter {flex-basis: 25%;

You can see a demo of the layout taking on a responsive feel here.

Using a CSS preprocessor

It goes without saying that developing the CSS is a lot easier if you make the use of a CSS preprocessor. I strongly recommend using one along with tools to aid in adding vendor prefixes. I personally use Stylus and the actual source for developing the styles wasn’t that large in comparison to the output.

Though, just for reference, here is the final CSS used for this post(minus the vendor prefixes and color theming to save some space).

To conclude

We’ve explored masonry layouts in pure CSS. I’m actually quite happy with the outcome. Pure CSS masonry layouts can work. I wouldn’t say they are flawless though. I can see potential problem scenarios. For example; if you wanted to display a blog with the content in date order, this would be tricky. I think it would require some extra thought. One solution would be to use columns and then populate them accordingly in date order. The result would be the newest item being in column one, the second newest in column two and so on (If you do really need your content in date order or from left to right, you might be best with a solution that introduces some JavaScript. I’ve put together a post for a possible solution here.)

I am going to continue working on the code and see what else I can come up with here(github). Feel free to fork a version or experiment with the demo code here (Note: If you’ve got this far and check out the demo you’ll notice some extra features such as focus on hover).

That’s it!

As always, any questions or suggestions, please feel free to leave a response or tweet me 🐦!