Lesson 25 - Home page intro

Course
Learn Eleventy From Scratch
Module
Front-End Build

We’re going to learn some CSS Grid to layout our home page intro responsively by creating a compositional layout block.


CSS Grid is a powerful layout tool that’s supported by the vast majority of browsers. Historically, it’s proven pretty difficult for folks to get their heads around, including me, so let’s try to change that.

In this lesson, I’m going to teach you how to build a pretty cool layout with CSS Grid that’s a bit of a brain teaser. Hopefully by the end of this lesson, you’ll feel a bit more comfortable with CSS Grid than when you started.

What we’re building permalink

On our home page we have a block called intro that morphs into different layouts depending on the viewport it finds itself in. Let’s take a quick look at it in a responsive design tool I like to use called Sizzy, which lets us create multiple different responsive contexts on one screen:

The home page intro morphs between a stacked layout and a complex grid layout based on the viewport. There are multiple devices such as iPhones, Macs and Android phones

There’s a lot going on, right? Let’s build it!

Wiring up our block and critical CSS permalink

This intro only appears on the home page, so we’re going to first create a home.scss file.

In your scss folder, create a new file called home.scss and add the following to it:

Code language
scss
// First up: config and functions
@import 'config';

// Set Gorko to output no CSS for this file
$outputTokenCSS: false;

// Next: pull in gorko for design tokens
@import '../../node_modules/gorko/gorko.scss';

// Pull in blocks
@import 'blocks/intro';

This is almost identical to our critical.scss file, but the difference here is that we’re instructing Gorko not to generate utility classes by setting $outputTokenCSS to false.

We need to add the block now, so create a new file in your blocks folder called _intro.scss and add the following to it:

Code language
scss
.intro {
  // Default is a single column layout where the header overlaps the media
  display: grid;
  grid-template-rows: get-size('700') minmax(0, 1fr) get-size('700') auto;
  grid-gap: get-size('500');

  // Force items to span 1 column
  > * {
    grid-column: 1;
  }
}

This is our default CSS. Even though we’re in a one column layout, we still use Grid. This is mainly because we want our headline to overlay the image. This is what we’re after:

The intro headline overlaying the background image

The first thing we do is set our rows up. The first row uses a size token (get-size('700')) because we want a bit of our image to display on its own first. Then we use minmax(0, 1fr) which allows the next row to grow to 1 part of the available remaining space. But importantly, it can be 0 if there’s nothing in it.

Following this is another explicitly sized row that uses a size token again, to allow some image to show through. This is followed by an auto row for the content and button.

We then use a direct child selector: > * to select only the direct children and force them to span the entire width with grid-column: 1.

Right, let’s get funky now.

Open up eleventy-from-scratch/src/scss/blocks/_intro.scss and just before the end of the block closing bracket, at around line 11, add the following:

Code language
scss
&__header {
  padding: get-size('300') get-size('500');
  background: rgba(get-color('tertiary'), 0.95);
  z-index: 1;
  grid-row: 2;
  margin: 0 get-size('500'); // Adds a horizontal gutter

  // Prevents it from stretching to fill the space
  align-self: center;
}

&__heading {
  em {
    font-style: normal;
    display: block;

    // The weight change creates a weird indent, so this
    // optical adjustment fixes it
    transform: translateX(-3px);
  }
}

&__media {
  grid-row: 1/4;
  position: relative;

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

&__content {
  grid-row: 4;

  p {
    max-width: 30ch;
  }
}

Brush aside some of the core visual CSS for a moment, because that stuff is pretty self-explanatory. Instead, focus on the grid-row for each of these elements, where defined. As explained before, we’re specifically telling our elements which row to place themselves on.

For example, we wanted the &__header to appear over the image. The first step to doing that is telling the header to sit in row 2, with grid-row: 2. Then, we tell our image to span from row 1 to the start of row 4 with grid-row: 1/4.

Understanding how grid rows, columns and grid lines work is really hard. I strongly recommend that you check out this fantastic resource, by Rachel Andrew, called Grid By Example.

I constantly refer to it myself.

Right, let’s expand this layout for larger screens.

Open up eleventy-from-scratch/src/scss/blocks/_intro.scss and straight after the last bit of code we added, on around line 52, add the following:

Code language
scss
// Switch to an inline layout with some vert space
// above the header and content
@include media-query('md') {
  grid-template-rows: get-size('500') auto auto auto;
  grid-template-columns: minmax(15rem, 1fr) 2fr;

  &__header {
    padding: get-size('500');
    margin: 0;
    grid-column: 1/3;
    justify-self: start;
    align-self: end;
  }

  &__media {
    grid-column: 3/2;
    grid-row: 1/5;
  }

  &__content {
    grid-row: 3/4;
    grid-column: 1;
  }
}

Here we’re expanding the layout for our md breakpoint, which is essentially tablet and up. Notice the grid-template-columns and grid-template-rows part? What we’re doing here is making our grid 4 rows high, with the first row being explicitly sized. This gives us a nice little vertical offset. Then, we’re making this a two column layout.

We want the content to be narrower than the image so we’re splitting it 1fr and 2fr. Remember, an fr is exactly one part of the available remaining space. We’re doing a magic trick here, too, by using minmax to say “make sure the element is at least 15rem wide. If it is, fill the space up to 1fr only”. This allows us some control to make sure that the content will still be legible if things get a bit tight.

Let’s take a look at this diagram where I’ve drawn the grid lines:

The larger, expanded layout has lines that create the 4 row and two column layout

The fourth row is chopped off the end of this viewport, but hopefully those lines help you to visualise how the grid now looks.

In the code above, we’re setting both rows and columns for our elements to place them where we want to place them. As always, the &__header gives us the most interesting CSS. We tell it to fill the entire grid with grid-column: 1/3. You might be thinking “that’s weird because the grid is only two columns wide”, right? By using 3, we are saying, “end on grid line 3” which sits at the end of our defined grid.

We use justify-self: start to break the default justification of stretch. This means our header will only display as wide as the content within it. This works very much the same as flexbox does.

Finally, we achieve the overlap again because our header and image share the same column and row space. We make the image fill all the rows by setting grid-row: 1/5. This works the same as the header and its columns.

Ok, nearly there. Let’s wrap up by adding some code for larger breakpoints. Open up eleventy-from-scratch/src/scss/blocks/_intro.scss and add the following after the last bit of code, on around line 78:

Code language
scss
// Flip the ratio for larger breakpoints
@include media-query('lg') {
  grid-template-columns: 1fr minmax(44rem, 1fr);

  // Because it's so large, it make sense to limit the image height too
  &__media {
    height: 28rem;
  }
}

Because we have more room now, we change the column layout to be more 50/50 instead. We use minmax to make sure our image is at least 44rem wide. Then, finally, we give our image a fixed height.

When all of that is in, your _intro.scss file should look like this:

Code language
Diff - don’t copy
.intro {
  // Default is a single column layout where the header overlaps the media
  display: grid;
  grid-template-rows: get-size('700') minmax(0, 1fr) get-size('700') auto;
  grid-gap: get-size('500');

  // Force items to span 1 column
  > * {
    grid-column: 1;
  }

  &__header {
    padding: get-size('300') get-size('500');
    background: rgba(get-color('tertiary'), 0.95);
    z-index: 1;
    grid-row: 2;
    margin: 0 get-size('500'); // Adds a horizontal gutter

    // Prevents it from stretching to fill the space
    align-self: center;
  }

  &__heading {
    em {
      font-style: normal;
      display: block;

      // The weight change creates a weird indent, so this
      // optical adjustment fixes it
      transform: translateX(-3px);
    }
  }

  &__media {
    grid-row: 1/4;
    position: relative;

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

  &__content {
    grid-row: 4;

    p {
      max-width: 30ch;
    }
  }

  // Switch to an inline layout with some vert space
  // above the header and content
  @include media-query('md') {
    grid-template-rows: get-size('500') auto auto auto;
    grid-template-columns: minmax(15rem, 1fr) 2fr;

    &__header {
      padding: get-size('500');
      margin: 0;
      grid-column: 1/3;
      justify-self: start;
      align-self: end;
    }

    &__media {
      grid-column: 3/2;
      grid-row: 1/5;
    }

    &__content {
      grid-row: 3/4;
      grid-column: 1;
    }
  }

  // Flip the ratio for larger breakpoints
  @include media-query('lg') {
    grid-template-columns: 1fr minmax(44rem, 1fr);

    // Because it's so large, it make sense to limit the image height too
    &__media {
      height: 28rem;
    }
  }
}

Now the last thing we need to do is define a page critical style for our home page. We covered this in the last module when we set our Sass up. The Sass part is now done, we just need to make base.html aware that there’s a critical CSS file to add to the pile.

Open up eleventy-from-scratch/src/_includes/layouts/home.html and on line 2 add the following:

Code language
HTML
{% set pageCriticalStyles = ['css/home.css'] %}

Your intro layout should look like this, when you open http://localhost:8080:

The intro layout looking perfect on the home page in a browser

Wrapping up permalink

That was a heck of a lesson, right? I really want you to walk away from this course not just with kickass Eleventy knowledge, but also kickass front-end, specifically CSS, knowledge.

We’re going to fill out the rest of the home page CSS in the next lesson which—you guessed it—will have lots more grid!

Next up: Lesson 26 - Home page panels

We’ve got 3 different types of panel on our home page, so let’s style them all up in one lesson.

Get started

Help! My version of this lesson isn’t working.

Don’t panic, you can download the working project here! Once they are downloaded: replace your project with these files and run npm install. You can read more information here.

Download project files

Hello, I’m Andy and I’ll help you build fast, accessible websites & design systems.

I’m a freelance CSS and design systems consultant, based in the UK. I specialise in design systems and creative web design, such as landing pages and campaign work.

I’m currently helping Google by refactoring the CSS and creating a design system for web.dev, but I have availability for projects such as small websites, landing pages and consultancy. I will have full availability for larger projects in January 2022.

Hire me