DEV Community

loading...
Cover image for 3 Ways to create a Navbar (CSS Grid Layout)

3 Ways to create a Navbar (CSS Grid Layout)

crayoncode
All about coding (and occasionally other topics) and explaining it through beautiful visualizations.
・6 min read

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):

Result

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.

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
Enter fullscreen mode Exit fullscreen mode

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:

:root {  
  --background-color: #2b2b2b;
  --foreground-color: rgba(255, 255, 255, 0.7);
  --transition: 250ms ease-out;
}
Enter fullscreen mode Exit fullscreen mode

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:

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

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

  &.version-3 {
    --color: #22d1d3;
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}
Enter fullscreen mode Exit fullscreen mode

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:

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%);
}
Enter fullscreen mode Exit fullscreen mode

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:

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; 
}
Enter fullscreen mode Exit fullscreen mode

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:

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;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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

  .nav {
    grid-area: nav;
  }

  .user {
    grid-area: user;
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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%;
      }
    }
  }

Enter fullscreen mode Exit fullscreen mode

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.

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);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

header {
  .user {
    box-shadow: inset 0px 0px 0px 1px var(--color);
  }
}
Enter fullscreen mode Exit fullscreen mode

Discussion (2)

Collapse
bariscc profile image
Baris Can Ceylan

Good tips on grid declarations! Similar to flex reverse, grid-template-area is powerful, especially when you need to re-order your grids on multiple breakpoints.
Note that the element order in html doesn't change, so screen readers and focus will follow DOM flow.

Collapse
crayoncode profile image
crayoncode Author

Thanks for positive feedback and for pointing out the focus order, I wasn't aware of that. 😅 A potentially confusing focus order is actually something that would limit using grid areas to reorder elements for me in some situations.