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