Newspaper design with CSS grid and border lines

Marco Troost
7 min readSep 18, 2019

--

Recently I had to craft a newspaper-like design that featured multiple row,- and column spans with divider-lines in between. If you’ve been around for a while, then perhaps you know that this could be a potential nightmare to code with regular CSS.

EDIT: This article has been published on CSS-Tricks.com! You can read the redacted version @ https://css-tricks.com/techniques-for-a-newspaper-layout-with-css-grid-and-border-lines-between-elements/

Newspaper design with divider-lines

Business requirements:

  • Show the outlines of the grid;
  • Columns can be wider, or longer than others;
  • Divider lines must be shown between the various blocks;

CSS-grid; old meets new

The reason newpaper-layouts could cause headaches is that everyday CSS is one-dimensional, meaning that content-cells are distributed on either a horizontal,- or on a vertical plane.

One would want properties good ol’ html-tables once provided: row,- and colspan’s to stretch cells in all directions. One would also want the benefits of modern day css, with all it’s responsiveness and flexible boxes that can grow to fill available spaces.

CSS grid combines the best of tables with the best of flex-boxes. Even better even, since grid provides a nifty feature for creating gutters between cells called “grid-gap”. Powerful as this may be, how does one create divider-lines exactly in the middle of those gutters?

This article describes three techniques how to create border-lines that are exactly in the middle of the gaps between content-cells.

#1: The ‘faux’ column technique

This solution creates ‘faux’ columns for creating vertical lines, and placing a grid on top of that. Horizontal dividers are painted if needed. N.B. ‘faux’ columns are created by using pseudo selectors in the grid-container.

HTML

<div class="frontpage">
<div class="fp-cell fp-cell--1">
<div class="fp-item">1</div>
</div>
<div class="fp-cell fp-cell--2">
<div class="fp-item">2</div>
</div>
<div class="fp-cell fp-cell--3">
<div class="fp-item">3</div>
</div>
<div class="fp-cell fp-cell--4 fp-cell--border-top">
<div class="fp-item">4</div>
</div>
<div class="fp-cell fp-cell--5 fp-cell--border-top">
<div class="fp-item">5</div>
</div>
<div class="fp-cell fp-cell--6 fp-cell--border-top">
<div class="fp-item">6</div>
</div>
<div class="fp-cell fp-cell--7 fp-cell--border-top">
<div class="fp-item">7</div>
</div>
</div>

CSS

$grijs_03: #efefef;
$grijs_15: #DADCE0;

.frontpage {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-column-gap: 32px;
border-top: 1px solid $grijs_15;
border-bottom: 1px solid $grijs_15;
overflow: hidden;

&:before,
&:after {
position: absolute;
top: 0;
height: 100%;
content: '';
}

&:before {
right: 50%;
width: calc(25% + 8px);
border-left: 1px solid $grijs_15;
border-right: 1px solid $grijs_15;
}

&:after {
right: 0;
width: calc(25% - 8px);
border-left: 1px solid $grijs_15;
}
}

.fp-cell {
position: relative;
z-index: 2;
padding: 16px 0;
background-color: #fff;
}

.fp-cell--border-top {
&:before {
content: '';
position: absolute;
top: 0;
left: -16px;
right: -16px;
border-top: 1px solid $grijs_15;
}
}

.fp-cell--1 {
grid-row: 1 / span 3;
}

.fp-cell--2 {
grid-row: 1 / span 2;
grid-column: 2 / span 2;
}

.fp-item {
background-color: $grijs_03;
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
height: 100%;
}

Result

The grid with divider lines and content placeholders

Setting up the lines between the columns

Create a four-columned container using display: grid and :before and :after pseudo-selectors to create 2 columns of 100% height of the container.

‘faux’ vertical grid

HTML:

<div class="frontpage"></div>

CSS:


$grijs_15: #DADCE0;

.frontpage {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-column-gap: 32px;
border-top: 1px solid $grijs_15;
border-bottom: 1px solid $grijs_15;
overflow: hidden;

&:before,
&:after {
position: absolute;
top: 0;
height: 100%;
content: '';
}

&:before {
right: 50%;
width: calc(25% + 8px);
border-left: 1px solid $grijs_15;
border-right: 1px solid $grijs_15;
}

&:after {
right: 0;
width: calc(25% - 8px);
border-left: 1px solid $grijs_15;
}
}

Note: 25% of the container doesn’t take out gutter-width into account, so you have to compensate. This is calculated as: 25% minus (amount of gutters * gutter-width) / (amount of gutters * amount of columns). (3 * 48 / 3 * 4 = 12).

Create the grid

The design consists of 7 blocks of content. Place them in the container & give them a modifier class for future reference. Also make sure their z-index is higher than the the pseudo-selectors of the grid.

HMTL:

<div class="frontpage">
<div class="fp-cell fp-cell--1"></div>
<div class="fp-cell fp-cell--2"></div>
<div class="fp-cell fp-cell--3"></div>
<div class="fp-cell fp-cell--4"></div>
<div class="fp-cell fp-cell--5"></div>
<div class="fp-cell fp-cell--6"></div>
<div class="fp-cell fp-cell--7"></div>
</div>

Set the background color for the cells (“fp-cell”) to white. This way the vertical lines won’t show through. Also set the vertical padding for the cell to 16px, in order to match half of the gutter.

Give the first and second content-blocks their unique span characteristics as shown in the design.

CSS:

.fp-cell {
position: relative;
z-index: 2;
padding: 16px 0;
background-color: #fff;
}
.fp-cell--1 {
grid-row: 1 / span 3;
}

.fp-cell--2 {
grid-row: 1 / span 2;
grid-column: 2 / span 2;
}

Vertical divider-lines

If you look at the design, only the cells > 4 need a horizontal border. Give ’em a sweet modifier class.

HTML

<div class="frontpage">
<div class="fp-cell fp-cell--1"></div>
<div class="fp-cell fp-cell--2"></div>
<div class="fp-cell fp-cell--3"></div>
<div class="fp-cell fp-cell--4 fp-cell--border-top"></div>
<div class="fp-cell fp-cell--5 fp-cell--border-top"></div>
<div class="fp-cell fp-cell--6 fp-cell--border-top"></div>
<div class="fp-cell fp-cell--7 fp-cell--border-top"></div>
</div>

CSS

.fp-cell--border-top {
&:before {
content: '';
position: absolute;
top: 0;
left: -16px;
right: -16px;
border-top: 1px solid $grijs_15;
}
}

(The negative margins are half of the gutter-width).

#2 The background-color technique

Another way to create dividers is to utilise grid-gap itself. This solution doesn’t necessarily create ‘real’ distance between cells, but rather uses grid-gap to leave some blank space where the background-color of the grid can shine through. Gutter-width is delegated to padding within the grid-cells.

HTML

<div class="container">
<div class="frontpage">
<div class="fp-cell fp-cell--1">
<div class="fp-item">1</div>
</div>
<div class="fp-cell fp-cell--2">
<div class="fp-item">2</div>
</div>
<div class="fp-cell fp-cell--3">
<div class="fp-item">3</div>
</div>
<div class="fp-cell fp-cell--4">
<div class="fp-item">4</div>
</div>
<div class="fp-cell fp-cell--5">
<div class="fp-item">5</div>
</div>
<div class="fp-cell fp-cell--6">
<div class="fp-item">6</div>
</div>
<div class="fp-cell fp-cell--7">
<div class="fp-item">7</div>
</div>
</div>
</div>

CSS

$grijs_03: #efefef;
$grijs_15: #DADCE0;
.container {
overflow-x: hidden;
border-top: 1px solid $grijs_15;
border-bottom: 1px solid $grijs_15;
}

.frontpage {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1px;
margin: 0 -16px;
background-color: $grijs_15;
}

.fp-cell {
padding: 16px;
background-color: #fff;
}

.fp-cell--1 {
grid-row: 1 / span 3;
}

.fp-cell--2 {
grid-row: 1 / span 2;
grid-column: 2 / span 2;
}

.fp-item {
background-color: $grijs_03;
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
height: 100%;
}

Create a container for the grid

Since all cells have an extra 16px horizontal padding, the grid needs to be offset just as much. A wrapper container will take care of the overflow.

HTML

<div class="container">
<div class="frontpage">
...
</div>
</div>

CSS

.container {
border-top: 1px solid $grijs_15;
border-bottom: 1px solid $grijs_15;
overflow-x: hidden;
}
.frontpage {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1px;
background-color: $grijs_15;

margin: 0 -16px;
}

#3 The cell-border technique

Solution three focusses on appending each cell with a border-right & border-bottom. Grid-gap is (like technique #2) mimicked by adding padding to the cell-content, which also need to be wrapped in an extra container.

HTML

<div class="container">
<div class="frontpage">
<div class="fp-cell fp-cell--1">
<div class="fp-item">1</div>
</div>
<div class="fp-cell fp-cell--2">
<div class="fp-item">2</div>
</div>
<div class="fp-cell fp-cell--3">
<div class="fp-item">3</div>
</div>
<div class="fp-cell fp-cell--4">
<div class="fp-item">4</div>
</div>
<div class="fp-cell fp-cell--5">
<div class="fp-item">5</div>
</div>
<div class="fp-cell fp-cell--6">
<div class="fp-item">6</div>
</div>
<div class="fp-cell fp-cell--7">
<div class="fp-item">7</div>
</div>
</div>
</div>

CSS

$grijs_03: #efefef;
$grijs_15: #DADCE0;

.container {
border-top: 1px solid $grijs_15;
overflow-x: hidden;
}

.frontpage {
margin: 0 -17px 0 -16px;
position: relative;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
}

.fp-cell {
padding: 16px;
background-color: #fff;
border-right: 1px solid $grijs_15;
border-bottom: 1px solid $grijs_15;
}

.fp-cell--1 {
grid-row: 1 / span 3;
}

.fp-cell--2 {
grid-row: 1 / span 2;
grid-column: 2 / span 2;
}

.fp-item {
background-color: $grijs_03;
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
height: 100%;
}

Asymmetrical negative margin

Each cell is given a border on the right & on the bottom. The main trick here is the use of the (asymmetrical) negative margin on the grid. This is needed to compensate for the cell’s right border.

CSS

.frontpage {
margin: 0 -17px 0 -16px;
position: relative;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
}

Conclusion

Ockam’s razor stipulates that the simplest solution wins, which in our case would be technique #2. But then again, there is also merit in the other solutions (if only for the sake of playing around).

All techniques will work. Choosing one is dependant of your use-case. Technique 1 uses grid-gap as an actual gap, whereas techniques 2 and 3 are more easy to put your head around & easier to maintain.

Happy coding!

--

--

Marco Troost
Marco Troost

Written by Marco Troost

I’m an experienced UX Designer / Front-end developer in the South West of Holland with over 20 years experience building websites.

No responses yet