Grouping items with CSS Flexbox

How would you approach the markup and the CSS regarding the buttons below when given the following mockup?

An image of mockup interface consisting of three buttons in two clusters

Of course we’d like to have a responsive interface where Button 1 floats to the left and Button 2 and Button 3 together float to the right.

And here’s the markup:

<div class="toolbar">
    <button>Button 1</button>
    <button>Button 2</button>
    <button>Button 3</button>
</div>

I imagine the first idea would be using CSS display: flex – the Flexbox – to the .toolbar container which we are going to employ here as well. The next question is: how these buttons can be distributed? We cannot really rely on justify-content: space-between because in this case all buttons would be spaced uniformly, i.e., Button 2 would be in the middle, thus no visual grouping.

I’ve seen code where people work around this by actually grouping these buttons in containers so that there are only two descendants under the .toolbar container:

<div class="toolbar">
    <div class="left">
        <button>Button 1</button>
    </div>
    <div class="right">
        <button>Button 2</button>
        <button>Button 3</button>
    </div>
</div>

That would work, for sure, but can we do better? Can we group these buttons without modifying the HTML markup thus leaving it minimalistic with only those elements that are meaningful and necessary?

<div class="toolbar">
    <button>Button 1</button>
    <button>Button 2</button>
    <button>Button 3</button>
</div>

It turns out that we can. Flexbox provides wonderful CSS property for flex items: flex-grow. Using that property, we can determine whether our element stretches to the whole available space of the container or not. But which element do we stretch? In the end we want to have that space between the button groups, which element should it be that should be stretched?

Well, it should be ::before or ::after pseudo element. Given them content (content: '') , they behave as direct first or last child of the container. Of course, by default both ::before and ::after pseudo-elements will be rendered at the beginning or at the end, respectively, of the container element. But what we want is this:

This is where we want to place our stretching pseudo element – in the middle between both button groups

Thankfully, there’s another CSS property from the Flexbox (or Grid for that matter) family that we can use: order. order allows us to arbitrary change the visual layout order of elements inside our flex container.

With everything mentioned above, the most basic solution could look like this:

.toolbar {
  display: flex;
  flex-direction: row;
}

.toolbar::before {
  content: '';
  flex-grow: 1;
  order: 2;
}

.toolbar>button:first-child {
  order: 1;
}

.toolbar>button:nth-child(2),
.toolbar>button:nth-child(3){
  order: 3;
}

One great thing about order property is that we don’t have to specify unique value for each element we want to reposition. `My personal intuitive model is to think about order as something that identifies a group holding elements together – visual layout order of the elements with the same order property value is then determined by their source order in the markup (now that’s a mouthful).

So in this case we’re interested in three groups: “left” (order: 1), “stretching center” (order: 2), and “right” (order: 3), and that’s OK if one of these properties are specified for more than one element (or, more precisely, CSS selector). This means that we can make our CSS more generic, albeit by adding class names to our buttons in the markup:

<div class="toolbar">
    <button class="toolbar-left">Button 1</button>
    <button class="toolbar-right">Button 2</button>
    <button class="toolbar-right">Button 3</button>
</div>

.toolbar {
  display: flex;
  flex-direction: row;
}
.toolbar::before {
  content: '';
  order: 2;
  flex-grow: 1;
}
.toolbar-left {
  order: 1;
}
.toolbar-right {
  order: 3;
}

Check out how it looks on JSBin. Of course we can also introduce another group using ::after pseudo-element as well thus having even three element groups.

So there you have it – a method how we can freely reposition our elements inside the toolbar or button row of a dialog without introducing *any* additional markup to group the elements.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.