Recreating Van de Graaf's canon for the web with CSS Grid
Introduction
I first stumbled on Van der Graaf's theory of page layout a couply of years ago, in an article called “The Secret Law of Page Harmony”. Now, I'm not really a believer in the golden ratio theory this ‘secret law’ is based on. Some believers think things designed using the golden ratio are inherently, subconsciously beautiful to the human eye. I do find the golden ratio a useful tool for your arsenal, and I do use it sometimes, often for expressive type scales.
Typography on point
When I'm working on (new) layout techniques, I like to get my typography on point first, because typography plays such a big role in how layout looks and feels. The Van der Graaf layout is meant to scale with the paper you use it on. To complement the Van der Graaf-canon, I'll base my typographic scale on the golden ratio (sometimes called the golden section).
In the browser, layouts are inherently scalable with the canvas, the viewport and beyond. As you might already know, typography can also scale with the viewport size, using vw
and vh
units. To complement the Van der Graaf-canon, I'll base my typographic scale on the golden ratio (sometimes called the golden section).
Using Modular Scale, I end up with the following typographic scale. I listed only the elements I'm sure I'll use here. I'll add elements to the stylesheet as I go.
HTML element | Font size |
---|---|
h1 | 4.236em |
h2 | 2.618em |
h3 | 1.618em |
h4, p | 1em |
I disregard h5
and h6
because I don't often use them. The h4
and p
elements use the same font size, but will be set apart by different styling.
As a starting point, the elements will all have margins of 1.5 × and 0.75 × their font size at the top and bottom respectively. This can be changed as necessary later.
h1, h2, h3, h4, p {
margin: 1.5em 0 0.75em;
}
h1 {
font-size: 4.236em;
}
h2 {
font-size: 2.618em;
}
h3 {
font-size: 1.618em
}
h4, p {
font-size: 1em;
}
You'll have noticed that I haven't used any viewport-based units. Zell Liew wrote about a technique that allows you to use a single CSS declaration to make the whole scale respond to the viewport size. It looks like this.
html {
font-size: calc(1em + 0.25 * (100vw - 45em) / 35);
}
Please note that I'm skipping all of the cross-browser intricacies Zell does talk about in his blog post. I've already updated the declaration to my preferences, scaling the base font size from 1em
to 1.25em
between the viewport sizes of 45e
and 80em
.
Scaling the font size of the html
element, effects all of the elements that inherit font size from it. All of the elements I'm currently using ( h1
, h2
, h3
, h4
and p
) do inherit font size from their parent.
I'm not setting other font sizes anywhere else in the document tree, allowing the elements to scale reliably and predictably according to viewport size. Above the thresholds of the calc()
, I set the font size to fixed values. I use a base font size of 1em
below the viewport width threshold of 45em
, which I do not have to declare a rule for, as it is the browser default.
@media screen and (min-width: 45em) {
html {
font-size: calc(1em + 0.25 * (100vw - 45em) / 35);
}
}
@media screen and (min-width: 80em) {
html {
font-size: 1.25em;
}
}
Lost in translation
Now that the typography and a vertical rhythm have been set up using viewport units, we can start working on the Van der Graaf page layout. After remembering the article this article started with, I wanted to see if I could implement the medieval page layout theories detailed there for the web using the CSS Grid specification. Layouts very much like the Van der Graaf canon one have been in use by monks since the Middle Ages. They have been specifically constructed for layout on tangible media. For digital media, there are several difficulties to overcome.
First, I think that when discussing page layouts the most important difference between physical media and the web is that the web is vertically endless. It's a ‘page’ that doesn't ever stop. Pages in books have well defined bounds and sizes you can use to define a layout as well as vertical rhythm. We already defined the vertical rhythm for our typography, but we are going to have to translate the vertically ‘capped’ print layout somehow.
Second, we are, as is often the case, designing for unknown users with unknown devices in unknown contexts of use. This has to work well in any circumstance.
Building the grid
I started out building a minimal, mobile-first grid. The original canon is based on the spread of a book. For application to digital design we have to account for the fact that devices can either have a portrait or landscape orientation.
In Van de Graaf's page layout both pages of a spread have a 9×9 grid. Mobile devices are often held in portrait orientation, which closely resembles a single page. The right-handed page of the page layout will be our starting point. We'll account for landscape orientation later.
The grid will have 9 columns of equal-sized fractions of the width of the page. For the portrait layout, I'll only use the horizontal characteristics of the Van de Graaf canon.
article {
display: grid;
grid-template-columns: repeat(9, calc(100vw / 9));
}
article > * {
grid-column: 2 / span 6;
}
In the code snippet above, I assign every direct child of the article
to start at the second column in the grid, and tell it to span 6 columns (out of 9), just like in the Van de Graaf canon. And as easy as that, we've got a grid that deals with the horizontal spacing of the article.
Now that this is in place, we've got the ‘single-page’ layout down. This works fine on rotated (landscape) phones or other devices. I want the spread to appear on larger screens.
On screens with a landscape orientation that are bigger than 48em
, I want to respect the vertical rhythm of the Van de Graaf canon.
@media screen and (min-width: 48em) and (orientation: landscape) {
article {
grid-template-columns: repeat(18, calc(100vw / 18));
}
article section > * {
margin-top: calc(100vh / 9);
margin-bottom: calc((100vh / 9) * 2);
min-height: calc((100vh / 9) * 6);
}
}
I solved this by cheating with simple margins instead of CSS Grid rows to reduce complexity and not to cap the vertical height of my content to predefined CSS Grid rows.
Having the vertical rhythm down, I have to tell each and every element in the article where it belongs.
@media screen and (min-width: 48em) and (orientation: landscape) {
article > header {
grid-column: 11 / span 6;
}
article > section:nth-of-type(odd) {
grid-column: 3 / span 6;
}
article > section:nth-of-type(even) {
grid-column: 11 / span 6;
}
article > section:first-of-type,
article > section:nth-of-type(2) {
grid-row: 2;
}
article > section:nth-of-type(3),
article > section:nth-of-type(4) {
grid-row: 3;
}
article > section:nth-of-type(5),
article > section:nth-of-type(6) {
grid-row: 4;
}
}
That's it, a finished implementation of the Van der Graaf-canon for CSS Grid. You can clearly see how this wouldn't scale to any sort of production environment. The vertical page layout of the Van der Graaf-canon that works so well on tangible media, is also what makes it very hard and hacky to work with on digital media. In having to create predefined grid areas, I also had to cut up the article content by hand, picking arbitrary points to break into a next grid area. This causes the layout to look awkwardly empty on some breakpoints.
Lessons learned
The CSS Grid specification by itself isn't enough for editorial text layout with constrained vertical containers. You'd like an article to be able to flow through multiple pre-defined grid areas. Unfortunately, now you have to cut it up into separate areas by hand. In doing so, you will notice your areas look weird on some aspect ratios, due to overflowing. What I wanted to achieve is still out of reach, for now, unless I write some potentially horrible JavaScript, or do hours and hours of art direction on exactly fitting images and text length for sections of the page.
Other possible solutions
CSS Regions could be the answer to my troubles, but it will not be implemented for some time yet. As of yet, only Safari has full support. IE11 and Edge support it partially, other browsers not at all, not even behind a flag. The layout might be achievable using CSS Columns, although after a quick look, I couldn't figure out how to break the columns at a height of 100vh
. Scaling the layout down to one ‘page’, using only the left- or righthand side of the grid, solves all of our problems, although it beats the point of the exercise and will make for a quite ordinary layout.