Pinboard Layout Using CSS Grid

Two ghostly white figures in coveralls and helmets are softly dancing preserve and cherish that pale blue dot permanence of the stars Rig Veda Apollonius of Perga Orion's sword?

Paroxysm of global death descended from astronomers network of wormholes citizens of distant epochs dream of the mind's eye emerged into consciousness?

- Milk
-  Eggs
 - Sugar
- Cornflour

Javascript-free, responsive pinboard layout with overlapping elements using CSS Grid featuring images from PlaceIMG

One of the joys of CSS Grid is being able to easily create designs with overlapping content. As a demonstration we've expanded our CSS Grid masonry technique to create a responsive virtual pinboard effect.

The example

You can see this technique in action on our CSS Grid pinboard example page where you can just jump straight in by hitting view source if you are so inclined.

The grid setup

To make the pinboard we first create a responsive grid with enough rows and columns to allow us to lay out the items on the board and we name the grid lines. For our example we've created a 20 row / 20 column grid with a simple numbering system for the grid lines. The width is 100vw (that's the whole viewport) and the height is fixed at 56.3vw so we have a nice pinboard-like aspect ratio. Here's the code:

.s-grid {
    display: grid;
    height: 56.3vw;
    width: 100vw;
    grid-template-rows: [y0] 2vw [y1] 1fr [y2] 1fr [y3] 1fr [y4] 1fr [y5] 1fr [y6] 1fr [y7] 1fr [y8] 1fr [y9] 1fr [y10] 1fr [y11] 1fr [y12] 1fr [y13] 1fr [y14] 1fr [y15] 1fr [y16] 1fr [y17] 1fr [y18] 2vw [y19] 1fr [y20];
    grid-template-columns: [x0] 2vw [x1] 1fr [x2] 1fr [x3] 1fr [x4] 1fr [x5] 1fr [x6] 1fr [x7] 1fr [x8] 1fr [x9] 1fr [x10] 1fr [x11] 1fr [x12] 1fr [x13] 1fr [x14] 1fr [x15] 1fr [x16] 1fr [x17] 1fr [x18] 1fr [x19] 2vw [x20]; 

The notation in square brackets (e.g. [y0]) is the naming of the grid lines and the majority of our grid rows and columns are defined in fr units. An fr unit is a 'fraction' and it gives us an easy way to tell the grid to divide up automatically without having to manually work out percentages (lots more on fr units here).

As well as all those 1fr rows and columns you'll see that there are also 2 rows and 2 columns that are defined as being 2vw units in size. These rows and columns will be used to form the frame of our pinboard.

The last thing to notice is that the frame is defined between lines y18 and y19. This leaves us a free row between y19 and y20 that we can use to contain items which hang off the bottom of the pinboard.

Styling the pinboard

Next up we will style the pinboard itself as it makes it much easier to see what's going on when we lay out the items. Here's the HTML for the bare pinboard:

<div class="s-grid">
    <div class="block-rim">
    <div class="block-board">

block-rim and block-board are just two rectangles sat one on top of the other. As block-board appears after block-rim in the code it is placed on top of it by default. Here's the CSS (background images from Tiling Textures):

.block-rim { 
    grid-area: y0 / x0 / y19 / x20;
    background-image: url('/assets/pinboard/corkboard-rim.jpg'); 
.block-board {
    grid-area: y1 / x1 / y18 / x19;
    background-image: url('/assets/pinboard/corkboard.jpg'); 

Here we are defining the position and size of the two rectangles using row-start / column-start / row-end / column-end notation which is where named grid lines that we defined come in to play. So for block-rim the notation y0 / x0 / y19 / x20 means that the top left corner is where grid lines y0 and x0 cross, and the bottom right corner is where grid lines y19 and x20 cross (leaving the bottom row of our grid free for items to hang off the bottom). block-board is slightly smaller and centered over block-rim.

Add items to the pinboard

Adding basic items to the pinboard is simple – just create a block-level element (e.g. a div) within the grid, give it a unique class name and assign it to the appropriate area using row-start / column-start / row-end / column-end notation. Remember that elements defined later in the HTML will be on top of elements defined earlier if they overlap. Here's the basics for the four elements on our example pinboard – first the HTML:

<div class="block block-1">
    <p>Two ghostly white figures in coveralls and helmets are softly dancing preserve and cherish that pale blue dot permanence of the stars Rig Veda Apollonius of Perga Orion's sword?</p>
    <p>Paroxysm of global death descended from astronomers network of wormholes citizens of distant epochs dream of the mind's eye emerged into consciousness?</p>
<div class="block block-2">
    <img class="cover" src="" alt="" />
<div class="block block-3">
    <img class="cover" src="" alt="" />
<div class="block block-4">
    <p>- Milk<br/>- &nbsp;Eggs<br/>&nbsp;- Sugar<br/>- Cornflour</p>

The text in block-1 come from Sagan Ipsum. The extra block class that each element has will be used for some styling later, here's the layout aspect of the CSS first:

.block-1 {
    grid-area: y3 / x2 / y17 / x8; 
.block-2 { 
    grid-area: y11 / x5 / y16 / x10;
.block-3 {
    grid-area: y10 / x11 / y20 / x16;
.block-4 {
    grid-area: y4 / x13 / y11 / x18; 

Note that block-3 stretches all the way down to y20 so it's hanging below the bottom of our pinboard (which stops at y19).

Styling the items on the pinboard

All that's left to do now is to style the items on the pinboard so that they look, well, more like items on a pinboard.

First up we add a box-shadow and a thumb tack to each element (those items don't stay on the pinboard using magic, you know):

.block {
    position: relative;
    width: 100%;
    height: 100%;
    object-fit: cover;
    -webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2), inset 0 0 50px rgba(0, 0, 0, 0.1);
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.5), inset 0 0 50px rgba(0, 0, 0, 0.4);
.block:after {
    content: url('/assets/pinboard/thumb-tack-2.png');
    display: block;
    position: absolute;
    top: -12px;
    left: 49%;
    z-index: 800;

The thumb tack image was adapted by me from an original image by Marco Verch available on flickr under a Creative Commons 2.0 (attribution required) license.

Next, we make sure that the text fits nicely on the two text items and that it scales with the pinboard when the screen size is changed. We do this by defining the text size using vw units and padding in % (note that these styles are in addition to the styles we already have for these blocks). We will also style the backgrounds on these two elements to make them look like a sheet of lined paper and a Post-It note respectively:

.block-1 {
    padding: 7% 2% 2% 20%;
    background-color: #FFFF99;
    background-image: url('/assets/pinboard/old_notepaper_texture_by_polkapebble.jpg');
    background-size: cover;
    color: #1295e6;
    font-style: italic;
    font-size: 1.85vw;
.block-4 { 
    padding: 5%;
    background-color: #FFFF99;
    color: #1295e6;
    font-style: italic;
    font-size: 2.5vw;

The lined paper background image is from polkapebble on DeviantArt.

We also need to make sure that the two photographs on the pinboard respect the size of their defined grid areas and scale properly, like this:

img.cover {
    height: 100%;
    width: 100%;
    object-fit: cover;

For the example we are dynamically loading images from PlaceIMG.

Now we use a CSS transform to rotate each element slightly. We've used transform: rotate(0.5deg); with a slightly different rotation value for each element.

Just one thing left now – as it is block-4 (the Post-It note) overlays the thumb tack for block-3 (a photo). So, we'll move the thumb tack over slightly on block-3 so that it's visible, like this:

.block-3.block:after {
    left: 25%

And that's it! See it in action on the example page.

Taking it further

In practice you rarely want your pinboard to take up the full viewport width at larger screen sizes, so you might want to add a few extra style rules enclosed in a CSS media query to provide a maximum size. You can see this in action on this page if you take a look at the source code.

You can add as many things as you like to your pinboard but as the number of items goes up it can get tricky to handle. Also, the more grid lines you add the slower the rendering speed will be, so if you are going to add a lot of items then I suggest building a custom grid just like in our CSS Grid masonry layout article to keep the grid lines to a minimum. This is the approach we took when creating the project pinboard on the [RPress] home page – you can see the test grid we used to create the layout here if you are interested. The project pinboard also uses art direction via the <picture> element to provide a different layout for small screen sizes where the pinboard would look too busy.

Questions or comments?

Hit us up on Twitter @RefinedPractice, drop Paul Ratcliffe a line on LinkedIn or get in touch using the details below ↓.


Tagged: CSS Pinboard Masonry Design