Redesigning a website using CSS Grid and Flexbox

For the last 15 years, I've been using floats for laying out a web pages on dri.es. This approach to layout involves a lot of trial and error, including hours of fiddling with widths, max-widths, margins, absolute positioning, and the occasional calc() function.

I recently decided it was time to redesign my site, and decided to go all-in on CSS Grid and Flexbox. I had never used them before but was surprised by how easy they were to use. After all these years, we finally have a good CSS layout system that eliminates all the trial-and-error.

I don't usually post tutorials on my blog, but decided to make an exception.

What is our basic design?

The overall layout of the homepage for dri.es is shown below. The page consists of two sections: a header and a main content area. For the header, I use CSS Flexbox to position the site name next to the navigation. For the main content area, I use CSS Grid Layout to lay out the article across 7 columns.

Css page layout

Creating a basic responsive header with Flexbox

Flexbox stands for the Flexible Box Module and allows you to manage "one-dimensional layouts". Let me further explain that by using an real example.

Defining a flex container

First, we define a simple page header in HTML:

<div id="header">
  <div class="title">Site title</div>
  <div class="navigation">Navigation</div>
</div>

To turn this in to a Flexbox layout, simply give the container the following CSS property:

#header {
  display: flex;
}

By setting the display property to flex, the #header element becomes a flex container, and its direct children become flex items.

Css flexbox container vs items

Setting the flex container's flow

The flex container can now determine how the items are laid out:

#header {
  display: flex;
  flex-direction: row;
}

flex-direction: row; will place all the elements in a single row:

Css flexbox direction row

And flex-direction: column; will place all the elements in a single column:

Css flexbox direction column

This is what we mean with a "one-dimensional layout". We can lay things out horizontally (row) or vertically (column), but not both at the same time.

Aligning a flex item

#header {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

Finally, the justify-content property is used to horizontally align or distribute the Flexbox items in their flex container. Different values exist but justify-content: space-between will maximize the space between the site name and navigation. Different values exist such as flex-start, space-between, center, and more.

Css flexbox justify content

Making a Flexbox container responsive

Thanks to Flexbox, making the navigation responsive is easy. We can change the flow of the items in the container using only a single line of CSS. To make the items flow differently, all we need to do is change or overwrite the flex-direction property.

Css flexbox direction row vs column

To stack the navigation below the site name on a smaller device, simply change the direction of the flex container using a media query:

@media all and (max-width: 900px) {
  #header {
    flex-direction: column;
  }
}

On devices that are less than 900 pixels wide, the menu will be rendered as follows:

Css flexbox direction column

Flexbox make it really easy to build responsive layouts. I hope you can see why I prefer using it over floats.

Laying out articles with CSS Grid

Flexbox deals with layouts in one dimension at the time ― either as a row or as a column. This is in contrast to CSS Grid Layout, which allows you to use rows and columns at the same time. In this next section, I'll explain how I use CSS Grid to make the layout of my articles more interesting.

Css grid layout

For our example, we'll use the following HTML code:

<article>
  <h1>Lorem ipsum dolor sit amet</h1>
  <p class="description">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
  <figure class="right">
   <img src="./image.jpg">
   <figcaption>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</figcaption>
  </figure>
  <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</p>
  <footer>
    Some meta data
    Some meta data
    Some meta data
  </footer>
</article>

Simply put, CSS Grid Layout allows you to define columns and rows. Those columns and rows make up a grid, much like an Excel spreadsheet or an HTML table. Elements can be placed onto the grid. You can place an element in a specific cell, or an element can span multiple cells across different rows and different columns.

We apply a grid layout to the entire article and give it 7 columns:

article {
  display: grid;
  grid-template-columns: 1fr 200px 10px minmax(320px, 640px) 10px 200px 1fr;
}

The first statement, display: grid, sets the article to be a grid container.

The second statement grid-template-columns defines the different columns in our grid. In our example, we define a grid with seven columns. The middle column is defined as minmax(320px, 640px), and will hold the main content of the article. minmax(320px, 640px) means that the column can stretch from 320 pixels to 640 pixels, which helps to make it responsive.

On each side of the main content section there are three columns. Column 3 and column 5 provide a 10 pixel padding. Column 2 and columns 6 are defined to be 200 pixels wide and can be used for metadata or for allowing an image to extend beyond the width of the main content.

The outer columns are defined as 1fr, and act as margins as well. 1fr stands for fraction or fractional unit. The width of the factional units is computed by the browser. The browser will take the space that is left after what is taken by the fixed-width columns and divide it by the number of fractional units. In this case we defined two fractional units, one for each of the two outer columns. The two outer columns will be equal in size and make sure that the article is centered on the page. If the browser is 1440 pixels wide, the fixed columns will take up 1020 pixels (640 + 10 + 10 + 180 + 180). This means there is 420 pixels left (1440 - 1020). Because we defined two fractional units, column 1 and column 2 should be 210 pixels wide each (420 divided by 2).

Css grid layout columns

While we have to explicitly declare the columns, we don't have to define the rows. The CSS Grid Layout system will automatically create a row for each direct child of our grid container article.

Css grid layout rows

Now we have the grid defined, we have to assign content elements to their location in the grid. By default, the CSS Grid Layout system has a flow model; it will automatically assign content to the next open grid cell. Most likely, you'll want to explicitly define where the content goes:

article > * {
  grid-column: 4 / -4;
}

The code snippet above makes sure that all elements that are a direct child of article start at the 4th column line of the grid and end at the 4th column line from the end. To understand that syntax, I have to explain you the concept of column lines or grid lines:

Css grid layout column lines

By using grid-column: 4 / -4, all elements will be displayed in the "main column" between column line 4 and -4.

However, we want to overwrite that default for some of the content elements. For example, we might want to show metadata next to the content or we might want images to be wider. This is where CSS Grid Layout really shines. To make our image take up the entire width we'll just tell it span from the first to the last column line:

article > figure {
  grid-column: 1 / -1;
}

To put the metadata left from the main content, we write:

#main article > footer {
  grid-column: 2 / 3;
  grid-row: 2 / 4;
}
Css grid layout placement

I hope you enjoyed reading this tutorial and that you are encouraged to give Flexbox and Grid Layouts a try in your next project.

Better image performance on dri.es

For a few years now I've been planning to add support for responsive images to my site.

The past two weeks, I've had to take multiple trips to the West Coast of the United States; last week I traveled from Boston to San Diego and back, and this week I'm flying from Boston to San Francisco and back. I used some of that airplane time to add responsive image support to my site, and just pushed it to production from 30,000 feet in the air!

When a website supports responsive images, it allows a browser to choose between different versions of an image. The browser will select the most optimal image by taking into account not only the device's dimensions (e.g. mobile vs desktop) but also the device's screen resolution (e.g. regular vs retina) and the browser viewport (e.g. full-screen browser or not). In theory, a browser could also factor in the internet connection speed but I don't think they do.

First of all, with responsive image support, images should always look crisp (I no longer serve an image that is too small for certain devices). Second, my site should also be faster, especially for people using older smartphones on low-bandwidth connections (I no longer serve an image that is too big for an older smartphone).

Serving the right image to the right device can make a big difference in the user experience.

Many articles suggest supporting three image sizes, however, based on my own testing with Chrome's Developer Tools, I didn't feel that three sizes was sufficient. There are so many different screen sizes and screen resolutions today that I decided to offer six versions of each image: 480, 640, 768, 960, 1280 and 1440 pixels wide. And I'm on the fence about adding 1920 as a seventh size.

Because I believe in being in control of my own data, I host almost 10,000 original images on my site. This means that in addition to the original images, I now also store 60,000 image variants. To further improve the site experience, I'm contemplating adding WebP variants as well — that would bring the total number of stored images to 130,000.

If you notice that my photos are clearer and/or page delivery a bit faster, this is why. Through small changes like these, my goal is to continue to improve the user experience on dri.es.

A fresh look for dri.es

In 1999, I decided to start dri.es (formally buytaert.net) as a place to blog, write, and deepen my thinking. While I ran other websites before dri.es, my blog is one of my longest running projects.

Working on my site helps me relax, so it's not unusual for me to spend a few hours now and then making tweaks. This could include updating my photo galleries, working on more POSSE features, fixing broken links, or upgrading to the latest version of Drupal.

The past month, a collection of smaller updates have resulted in a new visual design for my site. If you are reading this post through an RSS aggregator or through my mailing list, consider checking out the new design on dri.es.

2018 dri.es redesign
Before (left) and after (right).

The new dri.es may not win design awards, but will hopefully make it easier to consume the content. My design goals were the following:

  • Improve the readability of the content
  • Improve the discoverability of the content
  • Optimize the performance of my site
  • Give me more creative freedom

Improve readability of the content

To improve the readability of the content, I implemented various usability best practices for spacing text and images.

I also adjusted the width of the main content area. For optimal readability, you should have between 45 and 75 characters on each line. No more, no less. The old design had about 95 characters on each line, while the new design is closer to 70.

Both the line width and the spacing changes should improve the readability.

Improve the discoverability of content

I also wanted to improve the discoverability of my content. I cover a lot of different topics on my blog — from Drupal to Open Source, startups, business, investing, travel, photography and the Open Web. To help visitors understand what my site is about, I created a new navigation. The new Archive page shows visitors a list of the main topics I write about. It's a small change, but it should help new visitors figure out what my site is about.

Optimize the performance of my site

Less noticeable is that the underlying HTML and CSS code is now entirely different. I'm still using Drupal, of course, but I decided to rewrite my Drupal theme from scratch.

The new design's CSS code is more than three times smaller: the previous design had almost 52K of theme-specific CSS while the new design has only 16K of theme-specific CSS.

The new design also results in fewer HTTP requests as I replaced all stand-alone icons with inline SVGs. Serving the page you are reading right now only takes 16 HTTP requests compared to 33 HTTP requests with the previous design.

All this results in faster site performance. This is especially important for people visiting my site from a mobile device, and even more important for people visiting my site from areas in the world with slow internet. A lighter theme with fewer HTTP requests makes my site more accessible. It is something I plan to work more on in the future.

Website bloat is a growing problem and impacts the user experience. I wanted to lead by example, and made my site simpler and faster to load.

The new design also uses Flexbox and CSS Grid Layout — both are more modern CSS standards. The new design is fully supported in all main browsers: Chrome, Firefox, Safari and Edge. It is, however, not fully supported on Internet Explorer, which accounts for 3% of all my visitors. Internet Explorer users should still be able to read all content though.

Give me more creative freedom

Last but not least, the new design provides me with a better foundation to build upon in subsequent updates. I wanted more flexibility for how to lay out images in my blog posts, highlight important snippets, and add a table of content on long posts. You can see all three in action in this post, assuming you're looking at this blog post on a larger screen.

A breakout year for Open Source businesses

I was talking to Chetan Puttagunta yesterday, and we both agreed that 2018 has been an incredible year for Open Source businesses so far. (Chetan helped lead NEA's investment in Acquia, but is also an investor in Mulesoft, MongoDB and Elastic.)

Between a series of acquisitions and IPOs, Open Source companies have shown incredible financial returns this year. Just look at this year-to-date list:

Company Acquirer Date Value
CoreOS RedHat January 2018 $250 million
Mulesoft Saleforce May 2018 $6,5 billion
Magento Adobe June 2018 $1,7 billion
GitHub Microsoft June 2018 $7,5 billion
Suse EQT partners July 2018 $2,5 billion
Elastic IPO September 2018 $4,9 billion

For me, the success of Open Source companies is not a surprise. In 2016, I explained how open source crossed the chasm in 2016, and predicted that proprietary software giants would soon need to incorporate Open Source into their own offerings to remain competitive:

The FUD-era where proprietary software giants campaigned aggressively against open source and cloud computing by sowing fear, uncertainty and doubt is over. Ironically, those same critics are now scrambling to paint themselves as committed to open source and cloud architectures.

Adobe's acquisition of Magento, Microsoft's acquisition of GitHub, the $5,2 billion merger of Hortonworks and Cloudera, or SalesForce's acquisition of Mulesoft endorse this prediction. The FUD around Open Source businesses is officially over.

From a world wide web to a personal web

Solid concept inrupt

Last week, I had a chance to meet with Inrupt, a startup founded by Sir Tim Berners-Lee, who is best known as the inventor of the World Wide Web. Inrupt is based in Boston, so their team stopped by the Acquia office to talk about the new company.

To learn more about Inrupt's founding, I recommend reading Tim Berners-Lee's blog or Inrupt's CEO John Bruce's announcement.

Inrupt is on an important mission

Inrupt's mission is to give individuals control over their own data. Today, a handful of large platform companies (such as Facebook) control the media and flow of information for a majority of internet users. These companies have profited from centralizing the Open Web and lack transparent data privacy policies on top of that. Inrupt's goal is not only to improve privacy and data ownership, but to take back power from these large platform companies.

Inrupt will leverage Solid, an open source, decentralized web platform that Tim and others have been developing at MIT. Solid gives users a choice of where their personal data is stored, how specific people and groups can access select elements, and which applications can use it. Inrupt is building a commercial ecosystem around Solid to fuel its success. If Solid and/or Inrupt are widely adopted, it could radically change the way web sites and web applications work today.

As an advocate for the Open Web, I'm excited to see how Inrupt's mission continues to evolve. I've been writing about the importance of the Open Web for many years and even proposed a solution that mirrors Solid, which I called a Personal Information Broker. For me, this is an especially exciting and important mission, and I'll be rooting for Inrupt's success.

My unsolicited advice: disrupt the digital marketing world

It was really interesting to have the Inrupt team visit the Acquia office, because we had the opportunity to discuss how their technology could be applied. I shared a suggestion to develop a killer application that surround "user-controlled personalization".

Understanding visitors' interests and preferences to deliver personalized experiences is a big business. Companies spend a lot of time and effort trying to scrape together information about its website's visitors. However, behavior-based personalization can be slow and inaccurate. Marketers have to guess a visitor's intentions by observing their behavior; it can take a long time to build an accurate profile.

By integrating with a "Personal Information Broker" (PIB), marketers could get instant user profiles that would be accurate. When a user visits a site, they could chose to programmatically share some personal information (using a standard protocol and standard data schema). After a simple confirmation screen, the PIB could programmatically share that information and the site would instantly be optimized for the user. Instead of getting "cold leads" and trying to learn what each visitor is after, marketers could effectively get more "qualified leads".

It's a win not only for marketers, but a win for the site visitor too. To understand how this could benefit site visitors, let's explore an example. I'm 6'5" tall, and using a commerce site to find a pair of pants that fit can be a cumbersome process. I wouldn't mind sharing some of my personal data (e.g. inseam, waist size, etc) with a commerce site if that meant I would instantly be recommended pants that fit based on my preferences. Or if the store has no pants that would fit, it could just tell me; Sorry, we currently have no pants long enough for you!. It would provide me a much better shopping experience, making it much more likely for me to come back and become a long-time customer.

It's a simple idea that provides a compelling win-win for both the consumer and retailer, and has the opportunity to disrupt the digital sales and marketing world. I've been thinking a lot about user-controlled personalization over the past few years. It's where I'd like to take Acquia Lift, Acquia's own personalization product.

Inrupt's success will depend on good execution

I love what Solid and Inrupt are building because I see a lot of potential in it. Disrupting the digital marketing world is just one way the technology could be applied. Whatever they decide to focus on, I believe they are onto something important that could be a foundational component of the future web.

However, it takes a lot more than a good idea to build a successful company. For startups, it's all about good execution, and Inrupt has a lot of work to do. Right now, Inrupt has prototype technology that needs to be turned into real solutions. The main challenge is not building the technology, but to have it widely adopted.

For an idea this big, Inrupt will have to develop a protocol (something Tim Berners-Lee obviously has a lot of experience with), build out a leading Open Source reference implementation, and foster a thriving community of developers that can help build integrations with Drupal, WordPress and hundreds of other web applications. Last but not least, Inrupt needs to look for a sustainable business model by means of value-added services.

The good news is that by launching their company now, Inrupt has put themselves on the map. With Tim Berners-Lee's involvement, Inrupt should be able to attract top talent and funding for years to come.

Long story short, I like what Inrupt is doing and believe it has a lot of potential. I'm not sure what specific problem and market they'll go after, but I think they should consider going after "user-controlled personalization" and disrupt the digital marketing world. Regardless, I'll be paying close attention, will be cheering for their success and hopefully find a way to integrate it in Acquia Lift!