Excellent Adventures in CSS Grid – ON TOUR!

Recently I did my first talk for about two years at Milton Keynes Geek Night, I was really nervous after such a long break but thoroughly enjoyed it! So much so I decided to take my talk on tour! Which is exciting! 🙂

There is a lot of code examples and links to extra reading in this talk, so this post acts as a “catch all” for all of those extra resources.

Here is the current version of the talk:

You can find the CodePen Collection here: https://codepen.io/collection/AvJKoR/

Complete Reading List:

Tour dates for 2019:

25th June – WordPress Birmingham
27th June – Milton Keynes Geek Night
17th July – Staffs Web Meetup, Stafford
29th August – Frontend Sheffield
17th September – Brum JS, Birmingham
26-27th November – Frontend Connect, Warsaw

CSS GRID Fallback for IE using SASS Lists

When placing items in a CSS Grid, in modern browsers we can rely on auto-placement to do most of the heavy lifting for us, however when providing support for IE10 & 11, we have to write CSS code to explicitly place all of your items in the Grid.

This is because auto-placement is not supported in IE10 & 11 and sadly, it is also one area where Autoprefixer can’t help us either 😦

Writing all of this additional code can often be a pain so I’ve created a utility using SASS Lists to help make it a little less painful.

But why can’t Autoprefixer help?

Autoprefixer in its simplest form will look at each line of CSS, check it against caniuse.com and if it is not supported by a certain browser, but a prefixed version is available, it will add a line that includes the vendor prefix. For example:

::placeholder {
  color: gray;
}

Would output the following after Autoprefixer has been run:

::-webkit-input-placeholder {
  color: gray;
}
:-ms-input-placeholder {
  color: gray;
}
::-ms-input-placeholder {
  color: gray;
}
::placeholder {
  color: gray;
}

Great right?

But back to CSS GRID…

If we wanted to have a grid of four items, two in each column and row, we can achieve that easily by defining the grid-template-columns and grid-template-rows on the container:

.grid-container {
    display: grid;
    grid-template-columns: repeat( 2, 1fr );
    grid-template-rows: repeat( 2, 1fr );
}

Thanks to CSS GRIDs auto-placement we don’t need to do anything else, each grid- item will be repeated twice across both columns and rows and will fit 1 fraction so will appear equal in height and width. YAY!

The problem with Internet Explorer

As mentioned at the beginning, Internet Explorer does not support auto-placement. To be able to use GRID, we have to explicitly position grid-items. If we don’t do that then all of the items will stack in the first cell of the grid 😦

This means thats our code goes from:

.grid-container {
    display: grid;
    grid-template-columns: repeat( 2, 1fr );
    grid-template-rows: repeat( 2, 1fr );
}

to:

.grid-container {
    display: -ms-grid;
    grid-template-columns: repeat( 2, 1fr );
    -ms-grid-columns: 1fr 1fr;
    grid-template-rows: repeat( 2, 1fr );
    -ms-grid-rows: 1fr 1fr;
}

.grid-item-1 {
    -ms-grid-column: 1;
    -ms-grid-row: 1;
}

.grid-item-2 {
    -ms-grid-column: 2;
    -ms-grid-row: 1;
}

.grid-item-3 {
    -ms-grid-column: 1;
    -ms-grid-row: 2;
}

.grid-item-4 {
    -ms-grid-column: 2;
    -ms-grid-row: 2;
}

😦

But it’s not all bad, looking at all of those -ms- vendor prefixes used, this is where Autoprefixer has stepped in and fixed it for us right? Sadly no.

Because we haven’t used grid-column and grid-row first, (why would we when we don’t need the grid items to be explicitly placed in any browsers other than IE? ) Autoprefixer can’t see that there is anything that needs to be prefixed.

So, thanks to IE there is now a lot more code for our grid which we have to add manually and it is all starting to get very messy…

SASS to the rescue!

There isn’t a lot we can do about the amount of code needed to ensure our Grid works in IE as well, but we can tidy it up using SASS Lists and a @for loop so that it looks better in our codebase like this:

$total-items: 4;
$ms-layout: 1 1,
            2 1,
            1 2,
            2 2;

@for $i from 1 through $total-items {
    $position: nth( $ms-layout, $i );

    .grid-item-#{$i} {
	-ms-grid-column: nth($position, 1);
	-ms-grid-row: nth($position, 2);
    }
}

There is quite a lot going on in this little snippet so let’s break it down a bit.

This is the number of grid-items on the grid.

$total-items: 4;

This is our SASS List, it contains four rows, each with the position of the grid-column and then grid-row.

$ms-layout: 1 1,
            2 1,
            1 2,
            2 2;

Next we start a loop using $total-items so that the loop does not execute more than four times. Loop through each row using nth to retrieve the values for each grid-item in the $ms-layout list that correlates with where in the loop we are, and create a new list called $position.

The new list contains only the two values relevant to that grid-item. For example grid-item-3 would have a sass list that looks like $position: 1 2;

@for $i from 1 through $total-items {
    $position: nth( $ms-layout, $i );

Finally, we output the column and row positions:

    .grid-item-#{$i} {
	-ms-grid-column: nth($position, 1);
	-ms-grid-row: nth($position, 2);
    }

Using nth again we get the first value of $position for the column and the second value for row. Done!

Here is a complete example using six grid-items:

I’m sure many of you are thinking “Why bother going to this extra effort?”

This technique doesn’t stop the need for all of those extra lines of code to ensure your grid works in IE, and once the SASS has been compiled it will still output all of those lines, so you could stop at writing the vendor prefixes for IE and it would be backwards compatible.

But, if you are like me and find yourself continually copying and pasting them from project to project, it does keep your SASS a little bit neater and for me that is always worth the effort 🙂

Some Further Reading:

CSS Multicolour border and gradient

Recently I was asked to create a container that had a top border with a gradient applied to fade the border from blue to purple. But, the container also needed a background gradient that matched the border, and also faded out to white before the content started. Like this:

There are lots of ways to apply a gradient to a border, many illustrated in this excellent CSS Tricks article, and it is pretty easy to apply gradients to a background-image by using linear-gradient now as well.

The difficulty came when I realised I wanted to apply three different affects (border gradient, background gradient, and fade to white) to the same container without adding any additional markup.

In the end I achieved this by making use of pseudo elements and applying only two effects in total. Here is my solution:

First I started with my container markup and base styles for that.

<div class="container">
</div>
.container {
   display: block;
   width: 100vw;
   height: 100vh;
}

For the sake of this demo, I have set my container to the full width and height of the screen. Next we set both a :before and an :after pseudo element on the container.

.container {
   [...]

   &::before,
   &::after {
     content: "";
     position: absolute;
     left: 0;
     right: 0;
   }
}

Now that we have the foundations set all that is left to do is apply the effects.

.container {
   [...]

   &::before {
     background-image: linear-gradient( to right, #6a26b5, #007bc2 );
     height: 100px;
     top: 0;
   }

   &::after {
     background-image: linear-gradient( to top, #ffffff, rgba( 255, 255, 255, 0.7 ) );
     height: 90px;
     top: 10px;
   }
}

To break this down, the :before sets the first of two background gradients. This gradient is linear and flows from purple on the left to blue on the right. It is positioned at the top of the container, because we set position: absolute on both, and is 100px in height (you could easily change this to em or whichever unit you are using in your project).

The :after sets the second background gradient. This gradient is also linear but flows from a solid white at the bottom to a white with an opacity of 0.7, using the rgba() colour function, to the top. Which, when overlaid over the top of the colour gradient set on the :before, creates the fade out affect.

The final effect to achieve here is to create the border with the same gradient. To do that, all that is needed is to move the start position of the white gradient so that instead of starting at 0 it starts lower and displays some of the original solid gradient. In this example, the border needed to be 10px so I added:

top: 10px;
height: 90px;

The top value is then subtracted from the value of the height so that both :before and :after effects finish in the some position and preserve the smoothness of the fade out. If you only needed a the border to be 5px, then it would look like:

top: 5px;
height: 95px;

The full CSS Looks like:

.container {
  display: block;
  width: 100vw;
  height: 100vh;

  &::before,
  &::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
  }

  &::before {
    background-image: linear-gradient( to right, #6a26b5, #007bc2 );
    height: 100px;
    top: 0;
  }

  &::after {
    background-image: linear-gradient( to top, #ffffff, rgba( 255, 255, 255, 0.7 ) );
    height: 90px;
    top: 10px;
  }
}

Here it is in action:

Using linear-gradient meant that although three effects were required, only two needed to be created. This gave a really small performance win because we’ve skipped a third effect that would have needed to be painted, and it is well supported by modern browsers as well. Only a few of the more advanced features are not fully supported by Microsoft IE & Edge. Check out the browser compatibility section on MDN for more advanced details.

Create a 404 page with editable content in WordPress

In most cases default content on a 404 page is all that would ever be needed, in a default 404.php it would look some thing like this:

<div class="page-content">
    <p><?php _e( 'It looks like nothing was found at this location. Maybe try a search?', 'your-textdomain' ); ?></p>

    <?php get_search_form(); ?>
</div>

However, occasionally there is a need to give the site owner the flexibility to change the content displayed on the 404 page.

This can be done in a number of different ways, but the simplest implementation I found is to search to see if a 404 page has been created in the admin and if so display the content of that page.

First of all, in functions.php add the following function:

function get_404_page() {
    $page_exists = get_page_by_title( '404' );

    if ( ! empty( $page_exists->ID ) ) {
        return $page_exists;
    } else {
        return;
    }
}

Let’s look at this a bit more closely.

$page_exists = get_page_by_title( '404' );

First, we are searching through the pages created in the admin to see if a page with the title of 404 has been created. Next we do a check to see if this page has been found or not.

if ( ! empty( $page_exists->ID ) ) {

If the page ID is not empty then we need to return the page as WP_Post object so that we can later use the post_content in our 404.php.

return $page_exists;

By using the function get_page_by_title() to find the page, we get the WP_Post object directly, which we then return for later use.

} else {
    return;
}

Otherwise we return nothing to prevent any php errors occurring when we use the function within our page.

Finally, to implement the function, open your 404.php in your theme and add the following line to the top of the file.

$error_content = get_404_page();

If the 404 page exists in the admin the value of $error_content will be the WP_Post object, otherwise it will be empty.

All that is left now is to display the editable content on the page if it exists.

<div class="page-content">
    <?php
    if ( $error_content ) {
        echo apply_filters( 'the_content', $error_content->post_content );
    }
    ?>
    <p><?php _e( 'It looks like nothing was found at this location. Maybe try a search?', 'your-textdomain' ); ?></p>

    <?php get_search_form(); ?>
</div>

And that’s it! You can of course, remove the default text and search form. You could also add in other content from the post object and customise it as much or as little as you want.