3 Ways to create a Navbar (CSS Grid Layout)
Tags: webdev, css, tutorial, ui
Published: 2021-01-29 10:54:55

Today let's build three different navbar layouts using the CSS grid layout and I'll show you, how easy it is to change the arrangement of the items without actually touching the markup.

Read the full article or watch me code this on Youtube (TL;DW):

{% youtube IrjSsWOuie4 %}

## Result

{% codepen https://codepen.io/crayon-code/pen/OJbLdpX %} 

## Markup

The markup is basically a `<header>` element with
- a logo (the feather)
- three navigation items and
- a highlighted item for the user menu.

Since we're going to produce three different layout versions, the for loop is there to prevent us from copy-pasting ourselves.

```pug
main
  // generates the navbar three times
  // each with different version classes
  // version-1, version-2 & version-3
  - for (let i=0; i<3; i++)
    header(class="version-" + (i+1))
      .logo
        i.fas.fa-feather-alt
      nav
        ul.nav
          li 
            a(href="#") 
              i.far.fa-chart-bar
              | Dashboard
          li 
            a(href="#") 
              i.far.fa-edit
              | Projects
          li 
            a(href="#") 
              i.far.fa-envelope-open
              | Posts
      a.user(href="#") 
        i.far.fa-user
        | Jane Doe
```

## Basics, Background & Variables

Let's first setup a few basic variables that define the background and foreground color, as well as some transition paramters which are used in multiple spots:

```scss
:root {  
  --background-color: #2b2b2b;
  --foreground-color: rgba(255, 255, 255, 0.7);
  --transition: 250ms ease-out;
}
```

Each navbar is also having its own highlight colors, which is why the `--color` variable is defined within the scope of each version's own CSS class:

```scss
header {
  &.version-1 {
    --color: #ba4aff;
  }

  &.version-2 {
    --color: #008aff;
  }

  &.version-3 {
    --color: #22d1d3;
  }
}
```

Let's also reset padding, margin and box-sizing on each element. Alternatively you can include a pre-made normalization/reset CSS file like normalize.css or sanitize.css. 

```scss
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
```

As a fancy bonus, here's the code for the background which is just three different gradients stacked on top of each other in different angles:

```scss
main {  
  background:
    linear-gradient(322deg, 
      #ba4aff, rgba(#ba4aff, 0) 70%),
    linear-gradient(178deg,
      #008aff, rgba(#008aff, 0) 70%),
    linear-gradient(24deg,
      #00ffc6, rgba(#00ffc6, 0) 35%);
}
```

## Header Grid Configuration (Outer Grid)

We're going to use two nested grid layouts. The outer one on the `<header>` element  is simply there for providing the general layout of the navbar, so in order to tell where the logo, the navbar and the user menu is located:

```scss
header {
  display: grid;
  gap: 0.5rem;
  
  padding: 0.5rem;
  width: 100%;
  min-width: 750px;
  border-radius: 0.5rem;
  
  background: var(--background-color);
  box-shadow: 2px 2px 8px 0px var(--background-color);
  
  font-family: "Lato", sans-serif; 
}
```

The basic principle is to provide for each version of a navbar a different configuration for the columns and areas of the grid layout. A template area is a named section of the grid layout and an element can be assigned to it by providing the name of the area to it. And that's why it works, to change position of elements purely based on the CSS code, because only by switching or changing the values in `grid-template-areas` the browser adapts the places of the element and the order inside the markup becomes irrelevant:

```scss
header {
  // logo (left), nav items (left), user menu (right)
  &.version-1 {    
    grid-template-columns: min-content auto max-content;
    grid-template-areas: "logo nav user";
    
    > * {
      place-self: start;
    }
  }
  
  // logo (left), nav items (centered), user menu (right)
  &.version-2 {    
    grid-template-columns: min-content auto max-content;
    grid-template-areas: "logo nav user";
    
    > * {
      align-self: center;
    }
    
    .user {
      justify-self: end;
    }
    
    .nav {
      justify-content: center;
    }
  }

  // user menu, nav items (centered), logo (right)
  &.version-3 {    
    grid-template-columns: max-content min-content auto min-content;
    grid-template-areas: "user nav . logo";
    
    > * {
      justify-self: end;
      align-self: center;
    }
  }
}
```

Pay close attention to the values in `grid-template-columns`.
- `min-content` is used mostly for the logo because it should always consume as less space as possible / needed.
- `max-content` is used mostly for the user menu, since there are two words, `min-content` word produce a word wrap, because in that case the column with is minimized. `max-content` behaves that way that it maximizes the space for the content, but uses only as much space, as required.
- `auto` in our case means that it takes the remaining space, which is just what we want for the navigation items.

So in the end, all we need to do is to assign those identifiers mentioned in the `grid-template-areas` to the parts of the navbar and that's all being needed to setup the basic layout of the navbar.

```scss
header {
  .logo {
    grid-area: logo;
  }

  .nav {
    grid-area: nav;
  }

  .user {
    grid-area: user;
  }
}
```

## Navigation Grid Configuration (Inner Grid)

The grid for the navigation items (`.nav`) is a nested grid layout inside the outer grid that defines the general arrangement (logo, navigation items, user menu) in the `<header>` element. It uses the CSS grid's auto flow mechanics. This means that no explicit order of columns is defined, but each direct descendant element of the grid container (`.nav`) is given its own column. We can tell the grid layout how to deal with elements that are not assigned to a certain cell or group of cells in the grid. `grid-auto-flow: column` tells it to create a new column for each unassigned element. Setting it to `row` would cause it to create a new row.
`grid-auto-columns` is there to tell the grid which size auto-generated columns have, which is pretty awesome in our case, since we can simply tell it, to fit the content without wrapping it.

```scss
header {
  .nav {
    grid-area: nav;
    
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    gap: 0.5rem;
    place-self: center;
    
    height: 100%;
    
    list-style: none;
    
    > li {
      display: inline-block;
    }
  }
}
```

## Link & Icon Styling

Now, let's get into the finer details of the styling. Each link in the header navbar is a flex-box that centers its content vertically and also having a little bit of padding and rounded corners.

The markup makes use of a few font-awesome icons, so each `<i>` tag is given the highlight color and rendered slightly smaller, such that it doesn't "overpower" the text.

```scss 
header {
  a {    
    // align content vertically
    display: flex;
    align-items: center; 
    
    padding: 0.5rem 1rem;
    border-radius: 0.5rem;
    
    text-decoration: none;
    color: var(--foreground-color);

    > i {
      margin-right: 0.5em;
      
      color: var(--color);
      
      font-size: 0.7em;
      
      transition: all var(--transition);
    }
  }
}
```

## Link Hover Effect

For the hover effect we're going to use a nice `clip-path` effect. Each link is given an `::after` pseudo-element having the highlight color as background color and a `clip-path` that slightly cuts of the bottom right corner. If the link is not hovered, its positioned to the left (invisible) and on hover it's shifted to the right such that it covers the link's area. The `z-index` setup makes sure that the hightlight does not cover the text.

```scss
header {
  a {
    transition: all var(--transition);
    
    position: relative;
    overflow: hidden;
    z-index: 1;
    --slantness: 4rem;
    &::after {
      content: '';
      position: absolute;
      z-index: -1;
      
      width: calc(100% + var(--slantness));
      top: 0%;
      bottom: 0%;
      left: calc((100% + var(--slantness)) * -1);
      clip-path: polygon(
        0% 0%, 100% 0%,
        calc(100% - var(--slantness)) 100%, 0% 100%
      );
      
      background: var(--color);
      opacity: 0;
      
      transition: all var(--transition);
    }
    
    &:hover {
      color: white;
      
      > i {
        color: var(--background-color);
      }
      
      &::after {
        opacity: 1;
        left: 0%;
      }
    }
  }
  
```

## Logo & User Menu

The logo is simply a feather icon from font awesome and is colored on hover. To give it a little bit of a prettier shape, the top left and right bottom corner is strongly rounded off, which fits the shape of the feather and provides a nice effect on hover.

```scss
header {
  .logo {
    grid-area: logo;
    place-self: center;
    
    margin-right: 1rem;
    padding: 0.25rem;
    border-radius: 0.25rem;
    border-top-left-radius: 50%;
    border-bottom-right-radius: 50%;
    
    color: var(--color);
    
    font-size: 2rem;
    
    transition: all var(--transition);
    
    &:hover {
      background: var(--color);
      color: var(--background-color);
    }
  }
}
```

To make the user menu stand out a little, is given a slight box shadow to the inside using the highlight color. The same effect could have been achieved using a border, but the advantage of a box shadow is, that it does not affect the box-model and therefore keeps the size of the element consistent with the others.

```scss
header {
  .user {
    box-shadow: inset 0px 0px 0px 1px var(--color);
  }
}
```