I have this web page to display the books I've read. Book covers often bring back memories and it's been great to scroll past them on my own page. It's also an opportunity to play around with book data. This week, I added a bit of page count-based visualisation for fun.
This is what it looks like: rectangles with colours based on book covers. Each relates to a specific book, and its size is based on that book's page count.
Flex grow is part of the CSS's flex layout mode. When you add
display: flex to a HTML element, it becomes a flex container, and with that, its direct children become flex items. Flex items behave differently from block or inline level children. The difference is that flex items can be layed out in a specific direction (horizontal or vertical) and sized flexibly. This system is called “flexible layout box model” or simply flexbox. It comes with sensible defaults as well as full control via a range of properties.
One of those properties is
flex-grow. This is what it does:
[flex-grow] specifies the flex grow factor, which determines how much the flex item will grow relative to the rest of the flex items in the flex container when positive free space is distributed
So let's say you have ten
div, where the
div is a flex container and the
spans flex items:
If you want one to proportionally take up double the space and another to take up triple the space, you'll give them a
flex-grow value of
/* item 2 */
flex-grow: 2; /* takes up double */
/* item 6 */
flex-grow: 3; /* takes up triple */
The others will then each take up one part of the remaining space (as if set to
The specification recommends generally using
flex instead of
flex-grow. This shorthand can take three numbers: a
flex-grow factor (how much should it be able to grow),
flex-shrink factor (how much should it be able to shrink) and a
flex-basis factor (what size should we start with before applying grow or shrink factors.
We can also give
flex just one positive number, to use that number as
flex-grow, and a default of “1” as
flex-shrink and “0” as the basis (meaning no initial space is assigned, all of the size is a result from the grow or shrink factor). In our case, we'll use that
flex shorthand, it's a sensible default.
Flex-grow for widths, aspect ratio for heights
My sites displays, like, 50 books for a given year. Each with a different amount of pages. Some are just over 100 pages, others are over 500. I wanted to use that number as the
flex-grow factor. This means some books claim space of
113, others claim
So that will claim 113 worth of space for the one book and 514 worth of space for the other. I used little magic for this, I just applied an inline
style attribute to each book in my template (Nunjucks), setting
flex dynamically. When they all have a number, they all take a proportion, and when all the numbers are similar amounts, you'll see that the size they take up is quite similar, except for a few outliers.
As mentioned above, these numbers represent a proportion of the total available space. You might wonder: which space? Flexbox can work in horizontal and vertical direction. Or, really, in inline and block direction, where inline is the direction in which inline elements appear (like
strong) and block the direction in which block-level elements appear (eg paragraphs and headings).
Flexbox only takes space in one direction. For my illustration, I used the default, which is the inline direction or
flex-flow: row (this is shorthand for a
flex-direction: row and
flex-wrap: nowrap). This means my
flex-grow values are about a proportion of “inline” space, in my site's case, this means horizontal space.
I also want each book to take up some vertical space, as with only a width, we would not actually see anything. I could set a fixed height (in fact, I did that for the screenshot above). But in this case, I want the height to have a fixed relationship to the width. The
aspect-ratio property in CSS is made for that: if an item has a size in one dimension, the browser figures out what the other dimension's size needs to be to meet a ratio you provide. In this case, the browser finds a height based on the width that it calculated for our proportion.
Ok, so let's add an aspect ratio:
/* flex: [set for this book as inline style] */
aspect-ratio: 1 / 4;
I also added a background-color that I have available in my template (it's a piece of image metadata that I get for free from Sanity).
Oh, but wait… they still have the same height! That's because by default, items are aligned to ”stretch”, they take up all available space in the opposite direction (block if items flow in inline direction, inline if items flow in block direction). When items stretch, remaining space is assigned back to them. In this case, that means they'll get the same height. Often super useful and quite hard to do pre-flexbox. But in this case, we don't want that, so we'll use
align-items to set our items to align at the “end” (also can be set to “start”, “center” and “baseline”):
What the books looks like for different values of align-items
In my case, I also added a max-width to avoid extreme outliers and I addded opposite
rotate transforms to even and odd items for funs:
This works with grids too
When I shared this, Vasilis rightly noted you could pull the same trick with grids. If you add
display: grid to an element it becomes a grid container and its children grid items. That sounds a lot like flexbox, but the main difference is that sizing isn't done from the items, it is done from the container. For a grid, you define columns (and/or rows) that have a size. The
fr is one of the units you can use for this, it's short for “fraction”. Pretty much like proportions in flexbox,
fr will let you set column/row size that is a proportion of the available space. If you set all your columns with
fr, each one will be a proportion of all available space in the relevant direction.
I usually use flexbox with much lower numbers, often under 10. But it turns out, it works fine with larger numbers, too. I have not done extensive performance testing with this though, there may be issues if used for larger sites with more complex data.