I’m available for freelance work. Let’s talk »

My flexbox layout

Friday 19 April 2024

I recently had to reverse engineer the layout of this site. I created it once upon a time, but had forgotten the details, and to save myself the work five years from now when I have to do it again, I’m noting down what I learned about how it works.

For pedagogical purposes, I made a simplified example of the layout: toystell.html. The layout depends on CSS’s flexbox capabilities. The full implementation on this site includes a responsive layout that changes as the width of the browser shrinks, but the simple example doesn’t include that.

There are five components to the layout: logo, banner, sidebar, content, and footer. The skeleton of the HTML is:

<body>
    <header id="banner">Top Banner</header>
    <main id="content">Content</main>
    <aside id="sidebar">Sidebar</aside>
    <footer id="footer">Footer</footer>
    <header id="logo"></header>
</body>

Notice that the order of the elements in the HTML isn’t the same as the order they appear on the page. Flexbox makes this possible. We start by making the body element a centered flex container:

body {
    max-width: 55rem;
    margin: 0 auto 1rem;
    display: flex;
    flex-flow: row wrap;
}
body > * {
    flex: 1 100%;
}

The container is set to hold flex items in rows that wrap. We’re going to end up with three rows: the banner, the sidebar and content, and the footer. The default flex spec for elements in the body is to take the full width of the container, though this will only apply to the banner and footer.

First the logo is positioned absolutely with a specific height and width. It’s not participating in the flex layout at all, but the other elements will be adjusted to fit neatly around it. It’s square, so its height and width are the width of the sidebar:

:root {
    --side-width: 10rem;
}
#logo {
    position: absolute;
    width: var(--side-width);
    height: var(--side-width);
    background-image: url(https://placehold.co/400/orange/white?text=Logo);
    background-size: contain;
}

Then the banner is added as a fixed-height flexbox item. But if we simply do that, its left edge will be in the same position as the logo’s left edge, and they will overlap. To prevent this and make them abut nicely, we give the banner a left margin equal to the logo’s width:

:root {
    --banner-height: 4rem;
}
#banner {
    order: 1;
    height: var(--banner-height);
    margin-left: var(--side-width);
}

Next comes the fixed-width sidebar. The banner took the full width of the container, so the natural place for the sidebar is just under the banner at the left edge. But we need it lower than that, just under the logo’s bottom edge. To make that work, we give it a top margin of enough to push it down:

#sidebar {
    order: 2;
    flex: 0 var(--side-width);
    margin-top: calc(var(--side-width) - var(--banner-height));
}

All of the flex items were told to take the full width, but the sidebar is our one element that has a fixed width. The top margin is the difference between the height of the logo and the height of the banner.

The content will fit next to the sidebar under the banner just where we want it. “flex: 1 0” means it starts with zero width, but gets all of the remaining space in the flex row, so it’s width grows to take the remaining space:

#content {
    order: 3;
    flex: 1 0;
}

Last comes the footer. It needs a left margin like the banner did so that it won’t overlap the sidebar, and we give the sidebar a negative bottom margin to scooch it up to avoid the height of the footer:

:root {
    --footer-height: 3rem;
}
#sidebar {
    margin-bottom: calc(0rem - var(--footer-height));
}
#footer {
    order: 4;
    margin-left: var(--side-width);
    height: var(--footer-height);
}

I’ve skipped other parts that are in the toy sample, like how each item has carefully chosen borders to form the narrow gutters in the layout. These are just the broad strokes of how the five big boxes negotiate the space in their unusual way. The full site includes responsiveness, and uses Sass compiled to CSS to do some of the heavy lifting.

Better CSS wizards than me probably see better ways to do it, but it works.

Comments

[gravatar]

It looks nice. It even renders okay without flex box support in older browsers. The only breakage is the div background colors not working and everything being displayed on top of the hilbert space filling curve background tiling. But it’s still readable.

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.