Dropping CSS Frameworks for Grid and Flexbox

I changed my mind recently and decided to rework my website with Tailwind CSS and use its built in grid and flexbox properties. [2024-1-17]

I was developing in a world full of CSS hacks going back from 2000 to 2010. I was used to floating, inline-blocking and clearfixes to manage layout. I had tried to learn to use flexbox a few years ago, but it was when it was first introduced and most of the properties were browser-prefixed and didn’t always work consistently. So I just gave up and put down front-end development down for a while I waited for things to mature.

I avoided the pain by subscribing to the camps that used things like Bootstrap, Foundation and Skeleton. These were safe harbors where I could build applications quickly and focus on providing solid backends.

My interest was sparked again when I walked over to my coworker and asked him what he was working on. He showed me two tutorials of CSS grid and flex and gave me a quick proof-of-concept of what they both could do together and I was blown away.

I decided to build a page without a CSS framework only using grid and flexbox. I have to say that I am very pleased with how straightforward and simple it was to make a functional layout without worrying too much about the structure of my HTML.

What does a CSS grid and flexbox workflow for a fixed-width layout look like?

Here’s the HTML:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Test Grid Layouts</title>
    <link rel="stylesheet" href="styles.css">
  </head>

  <body>
    <div class="container">
      <header>
        <nav>
          <div id="logo">Using Grid</div>
          <ul>
            <li><a href="#">About</a></li>
            <li><a href="#">Blog</a></li>
            <li><a href="#">Contact</a></li>
          </ul>
        </nav>
      </header>

      <main>
        <div class="content">
          <section class="hero">
            <h3>Color Me Black</h3>
          </section>

          <article>
            <h1>Title</h1>
            <p>I love content!</p>
          </article>
        </div>
      </main>

      <footer>
        <nav>
          <div class="siteinfo">Made by Angelo</div>
        </nav>
      </footer>
    </div>
  </body>
</html>

And the CSS:

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  font-family: 'Roboto', 'Helvetica', sans-serif;
}

.container {
  display: grid;
  min-height: 100%;
  grid-template-rows: 48px [stage] auto 48px;
  grid-template-columns: 100%;
}

#logo {
  color: #FFFFFF;
  font-size: 1.5em;
}

header {
  display: grid;
  grid-template-columns: auto [head-center] 832px auto;
  height: 48px;
  background-color: #000000;
}

header > nav {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: flex-start;
  align-items: center;
  grid-column-start: head-center;
  grid-column-end: span 1;
}

header > nav ul {
  margin: 0 0 0 auto;
}

header > nav ul li {
  display: inline;
  padding-left: 8px;
}

header > nav ul li a {
  color: #FFFFFF;
  text-decoration: none;
  text-transform: uppercase;
}

header > nav ul li a:hover {
  color: #AAAAAA;
}

main {
  display: grid;
  grid-template-columns: auto [main] 832px auto;
  grid-template-rows: auto;
  grid-row-start: stage;
  grid-row-end: span 1;
}

main > div.content {
  grid-column-start: main;
  grid-column-end: span 1;
}

main > div.content .hero {
  height: 300px;
  width: 100%;
  background-color: #000000;
  color: #FFFFFF;
}

main > div.content .hero h3 {
  padding: 16px;
}

footer {
  display: grid;
  grid-template-columns: auto [foot-center] 832px auto;
  grid-template-rows: 48px;
  background-color: #000000;
}

footer > nav {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: flex-end;
  align-items: center;
  grid-column-start: foot-center;
  grid-column-end: span 1;
}

footer > nav > .siteinfo {
  color: #FFFFFF;
}

I have some other nifty things in here to make things look pleasant, but we should really focus on where display: grid and display: flex is used.

The .container element is our top-level grid:

.container {
  display: grid;
  min-height: 100%;
  grid-template-rows: 48px [stage] auto 48px;
  grid-template-columns: 100%;
}

Here, we use grid-template-rows to define our top-level rows (from top to bottom). These can be a fixed size, a percentage or auto. Each specification under the property is mapped to a child item in the order of definition. For example, the first 48px entry is my first row, auto (which is aliased to stage) is my second row, and 48px is my final and last row. I’ve also specified that I want the grid-template-columns to expand the full width of my container.

Now that I’ve done this, I have to think about the direct child elements that I create from that grid element because all the properties I applied are to the direct children of this parent element.

My child elements (my grid items) from this parent of .container are header, main and footer. I define properties on these items to tell them where they should go on the grid.

Let’s take a look at main:

main {
  display: grid;
  grid-template-columns: auto [main] 832px auto;
  grid-template-rows: auto;
  grid-row-start: stage;
  grid-row-end: span 1;
}

I’m doing several things here. I’m defining another grid under the top-level .container grid and I’m telling this grid item to start where the stage row begins and then extend (or span) 1 cell space down from it. If I had not aliased the starting row to stage, then I would have to count the number of rows down I would want to start from and enter that number (in this case, it would have been 2).

This is basically all there is to it. While this is not like the HTML <table> tag, it’s mental model of placement is very similar. You can think of colspan and rowspan working very much like the span property above.

To me, the two most important concepts to get are the parent element where the grid layout is defined and the child elements where each “item” is defined. An item is a DOM element that is mapped as content to a certain location in the grid.

You can nest grid to get more complex layouts. I did this so I could extend the backgrounds of my header and footer across the width of my page, but also maintain an “internal” grid on the main element where content would be centered with margins that have an automatic width.

While I used grid to manage the layout, I used flex to manage the positioning behavior of child elements within the layout. This is not just vertical and horizontal positioning, but relative spacing between children and even ordering. Flex is great at this sort of thing and can (and has) been used for layouts, but in the end, it starts to really feel hacky as your DOM gets more complex. So use grid if you are trying to design big picture components.

For grid, there are also more things to think about like lines, tracks, areas and cells. I’ve also made heavy use of aliases. These are all things you should play with and understand.

So it’s best to get experimenting. Go ahead and copy the HTML directly to a file and save it as index.html and then save the CSS as styles.css and plop them in a folder.

Assuming you have Python 3 installed and you have a unix shell available you can serve these up:

$ cd YOUR_FOLDER
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Then open up a browser to http://localhost:8000 and start adjusting CSS values using the built-in developer console in your browser or edit the files directly on disk and hit refresh on the browser for every change you want to make.

Also, try it with some of the mobile views. You’ll quickly see the width is fixed. Can you make it work in a fluid or responsive way?

Here’s a hint: Look at the HTML and CSS of this blog site.

In Summary

There are lots of things you can do with grid. You can throw in media queries and you can make your layouts completely fluid if you wish. Most modern browsers support grid and flex.

Take a look at the grid guide and the flexbox guide or at the spec itself to get more information and most importantly – throw away your hacks!

References