Heyyy Marco
Heyyy Marco

Reputation: 753

Creating navbar with CSS grid

I'm creating a navbar made with CSS grid.

I decided making it with grid so I can re-arrange the sections (items) without modifying the html (just modify the CSS).

Then I created the grid with 3 areas: logo, menus, toggler.

And I added a bit of gap so each items won't stick together.

enter image description here

So far so good, until I tried to remove one/some sections, the gap still there even if the section was gone.

Then I tried to remove the gap and replace with a margin to each section. But the margin won't collapse at the beginning/ending of the grid. It behave differently than on regular block element.

I know it's more practical using a flexbox rather than a grid, but I prefer the grid because the section can be re-arranged without modifying the html. It's possible to move the logo at the top or somewhere else. Something impossible with flex.

Anyone can solve my grid-gap problem? Or maybe you've a different approach for creating the navbar?

See my sandbox:

.navbar {
  background: pink;
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: max-content auto max-content;
  grid-template-areas: "logo menus toggler";
  justify-items: stretch;
  align-items: stretch;
  column-gap: 20px;
}

.logo {
  background: green;
  grid-area: logo;
  width: 60px;
}

.menus {
  background: lightsalmon;
  grid-area: menus;
  display: flex;
  flex-direction: row;
  justify-content: start;
}

.menus * {
  padding: 5px;
}

.toggler {
  background: lightskyblue;
  grid-area: toggler;
}
<p>navbar with complete sections:</p>
<nav class="navbar">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without logo:</p>
<nav class="navbar">
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without toggler:</p>
<nav class="navbar">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
</nav>

Upvotes: 8

Views: 18552

Answers (4)

Temani Afif
Temani Afif

Reputation: 272797

In such case, you can rely on implicit columns. You only define one explicit column and you create new ones only when the elements are there. You can easily swap the positions with issue too

.navbar {
  background: pink;
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: 1fr; /* only column intially */
  grid-auto-flow:dense;
  justify-items: stretch;
  align-items: stretch;
  column-gap: 20px;
}

.logo {
  background: green;
  width: 60px;
  grid-column:-3; /* create an implicit one at the beginning */
}

.menus {
  background: lightsalmon;
  display: flex;
  flex-direction: row;
  justify-content: start;
  grid-column: 1; /* take the explicit one */
}

.menus * {
  padding: 5px;
}

.toggler {
  background: lightskyblue;
  grid-column:2; /* create an implicit one at the end */
}
<p>navbar with complete sections:</p>
<nav class="navbar">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without logo:</p>
<nav class="navbar">
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without toggler:</p>
<nav class="navbar">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
</nav>

<hr>
<p>navbar without toggler and log at the end </p>
<nav class="navbar">
  <div class="logo" style="grid-column:2">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
</nav>

<hr>

<p>navbar without logo and toogler at the beginning </p>
<nav class="navbar">
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler" style="grid-column:-3">
    X
  </div>
</nav>

Upvotes: 3

Masoud Keshavarz
Masoud Keshavarz

Reputation: 2234

Instead of column-gap: 20px; in this case you can try this:

nav > div:not(:first-child){
  margin-left:20px;
}

It adds margin for first level divs and it excludes the first div from selection.

.navbar {
  background: pink;
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: max-content auto max-content;
  grid-template-areas: "logo menus toggler";
  justify-items: stretch;
  align-items: stretch;
}

nav > div:not(:first-child){
  margin-left:20px;
}

.logo {
  background: green;
  grid-area: logo;
  width: 60px;
}

.menus {
  background: lightsalmon;
  grid-area: menus;
  display: flex;
  flex-direction: row;
  justify-content: start;
}

.menus * {
  padding: 5px;
}

.toggler {
  background: lightskyblue;
  grid-area: toggler;
}
<p>navbar with complete sections:</p>
<nav class="navbar">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without logo:</p>
<nav class="navbar">
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without toggler:</p>
<nav class="navbar">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
</nav>

Upvotes: 3

dale landry
dale landry

Reputation: 8600

You could just create css rule for each different nav as the grid-template-columns and grid-template-areas are somewhat baked in if you use the same class for each nav class.

grid-template-columns: max-content auto max-content;
grid-template-areas: "logo menus toggler";

are looking for three columns to throw a gap between.

If you edit each nav to match each elements column content it will display correctly without any need for hacks.

.navbar1 {
  background: pink;
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: max-content auto max-content;
  grid-template-areas: "logo menus toggler";
  justify-items: stretch;
  align-items: stretch;
  column-gap: 20px;
}

.navbar2 {
  background: pink;
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: auto max-content ;
  grid-template-areas: "menus toggler";
  justify-items: stretch;
  align-items: stretch;
  column-gap: 20px;
}

.navbar3 {
  background: pink;
  display: grid;
  grid-template-rows: auto;
  grid-template-columns: max-content auto;
  grid-template-areas: "logo menus";
  justify-items: stretch;
  align-items: stretch;
  column-gap: 20px;
}

.logo {
  background: green;
  grid-area: logo;
  width: 60px;
}

.menus {
  background: lightsalmon;
  grid-area: menus;
  display: flex;
  flex-direction: row;
  justify-content: start;
}

.menus * {
  padding: 5px;
}

.toggler {
  background: lightskyblue;
  grid-area: toggler;
}
<p>navbar with complete sections:</p>
<nav class="navbar1">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without logo:</p>
<nav class="navbar2">
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
  <div class="toggler">
    X
  </div>
</nav>

<hr>

<p>navbar without toggler:</p>
<nav class="navbar3">
  <div class="logo">
    LoGo
  </div>
  <div class="menus">
    <div>menu-1</div>
    <div>menu-2</div>
    <div>menu-3</div>
  </div>
</nav>

Upvotes: 0

ssbrear
ssbrear

Reputation: 138

This can absolutely be done with grid, and you're completely right about the strengths of grid being able to seemingly re-arrange html elements. I think the issue is that your column gap applies in all three cases since it is a part of your navbar class

.navbar {
    column-gap: 20px;
}

In the example below I created two grid-area containers using the same class. I applied the column-gap to the top container, but not the bottom container.

I also used justify-self: end for the blue div on the left to show you that there is no gap between columns, although you can also use the chrome dev tools to see that the top grid has gaps, while the bottom does not.

.container {
  display: grid;
  grid-template-areas: "left center right";
  margin-bottom: 40px;
}
.top {
  column-gap: 20px;
}
.bottom{
  column-gap: 0;
}

.d1 {
  grid-area: left;
  background: red;
}

.d2 {
  grid-area: center;
  background: red;
}

.d3 {
  grid-area: right;
  background: red;
}

.d4 {
  grid-area: left;
  background: blue;
  justify-self: end;
}

.d5 {
  grid-area: center;
  background: blue;
}

.d1,
.d2,
.d3,
.d4,
.d5 {
  width: 200px;
  height: 50px;
}
<div class="container top">
  <div class="d1"></div>
  <div class="d2"></div>
  <div class="d3"></div>
</div>

<div class="container bottom">
  <div class="d4"></div>
  <div class="d5"></div>
</div>

Upvotes: 1

Related Questions