#15DaysOfCSS Day 9: Day 9: Horizontal navbars with a button

Don't forget your Call To Action!

Donald Miller of Storybrand recommends a big call to action (CTA) in the upper right corner of your web page, usually in your navbar. It's a button that asks the visitor to do something: subscribe, buy now, get a quote, free trial, and so forth.

From a code perspective, this means we need to make one navigation item look different than the others.

Yesterday, we learned to make buttons from links. Today, we'll take that button code and place it in our navbar.

Adding a button to a non-responsive navbar isn't too tough, and we'll cover that today. However, when we make our navbars responsive (day 12) or add dropdowns (day 13) or move to a hamburger layout (day 14), the button may pose some interesting problems for us! We'll address those as we go.

Step 1: Set up the HTML

Nothing much to see here, except we've added a class of button to the last link in the navbar.

<nav>
  <ul>
    <li><a href="#">Home</a></li>
    <li><a href="#">Products</a></li>
    <li><a href="#">Services</a></li>
    <li><a href="#">About</a></li>
    <li><a href="https://learnwithjen.com/#sIBUEk" target="_blank" class="button">Buy Our Book!</a></li>
  </ul>
</nav>

Step 2: Set up the horizontal navbar

We added the default font styles to the document, and we've set up our links to display horizontally with Flexbox. This time, we've aligned the navbar to the left using justify-content: flex-start.

/* border box model, see Day 2 for details - https://jen4web.substack.com/p/box-model */

html {
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}

/* Google font - in CodePen, link it under Settings - CSS - external stylesheet */

body {
  font-family: 'Reggae One', cursive;
  margin: 0;
  padding: 0;
  font-size: 1.5rem;
}
/* get the navigation off the edges of our window - you may not need this outside of CodePen */
nav {
  margin: 5rem 0 0 1rem;
}

/* horizontal navbar, no bullets or margin/padding, align links to the left and give them space in between */
ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
  gap: 1rem;
}
/* colors, no underline, and BIG CLICKABLE AREA */
a {
  color: #17874B;
  text-decoration: none;
  display: block;
}
a:hover {
  color: #E05D06;
}

Step 3: Style the button

You can do this separately as its own CodePen, or style the button directly in the navbar. Don't worry too much about how the button aligns relative to the rest of the navbar - we'll fix it!

/* button colors, padding, round the corners, and add a border that feels button-ish */
.button {
  color: white;
  background-color: #17874B;
  padding: 0.2rem;
  border-radius: 5px;
  border: 2px outset gray;
}
.button:hover {
  color: white; /* without specifying, the orange color from the default hover state carries through */
  background-color: #0C552E;
  border: 2px inset gray;
}

Your navbar looks like this... not bad, but a little crooked.

Step 4: The good stuff - how do we get this lined up?

The problem we want to solve is pretty identifiable - the button isn't lined up with the rest of the navbar. Clearly we need to fix it.

Your first question should be what does success look like? You may need to draw this on paper. Get a very clear picture of what you want to achieve. We want the navbar to look like this:

In other words, we want the bottom of the text to align going across. We don't want the bottom of the button to align with the other navbar text. That's a legitimate possibility, but if we did that, it would still look crooked:

Now that you know that you want the bottom of the text to align going across, what caused that alignment to fail?

Step 4A: Examine Flexbox alignment

There are two areas to examine. First, let's look at our Flexbox layout and make sure that's working as expected. We now have one element (the button) that is taller than the others. However, we can't diagnose this problem by looking at what we have. Turn on some debugging borders!

li {
  border: 2px solid blue;
}
a {
  border: 2px solid gray;
}

This shows us where each flex-item (the <li>) is located in space, and it shows us where the contents (the <a>) are located inside. Apply these styles and get this:

By default, all content is aligned to the top of each flex-item.

In Flexbox there are two directions for alignment, the main axis, and the cross axis. The main axis is defined using the flex-flow or flex-direction property. We defined this as row, so the cross axis is column in this example.

Since we want to align the bottom of our text, we should move our cross axis to align at the bottom of each of the the flex items as well. justify-content is used to define the alignment of flex items on the main axis, while align-items is used to define flex item alignment on the cross axis. (CSS Tricks has the full Flexbox cheat sheet!)

Therefore, let's add this declaration to the ul style:

ul {
  align-items: flex-end;
}

This pushes all content to the bottom of each of our flex items. If we turn off our debugging borders, we're closer!

Step 4B: What's the difference between .button and the rest of our links?

The navigation was in perfect alignment until we added the .button class, and that's when it failed. Therefore, a logical place to look for a point of failure is with the button styles we added.

Most notably, we added this:

.button {
  padding: 0.2rem;
  border: 2px outset gray;
}

Well! If we've added some space to the bottom of our button, that would push it up slightly relative to our other links. We need to add a similar amount of space to our links:

a {
  padding-bottom: 0.2rem;
}

Oh, the result is SO CLOSE, but graphic designers will be quick to point out that it's not perfect!

It's off by... well, 2px. That's the width of the border on our button. But now we have a padding in rem, and a border in px. What could we do? Two different units of measure are difficult to combine.

One possibility (which works!) is to use margin to boost the links:

a {
  padding-bottom: 0.2rem;
  margin-bottom: 2px;
}

However, far more elegantly, this is a perfect situation to use the calc() function. calc() will happily add two numbers of different units:

a {
  padding-bottom: calc(0.2rem + 2px);
}

Woo hoo! Perfect!

Learn more about calc() at CSS Tricks.

What about align-self in Flexbox?

align-self allows you to change a single flex item's alignment on the cross axis (the column, in this case). Details at CSS Tricks.

While this would move the button up and down in space, it would probably move it too far relative to the other items. We only need the button to move a very small amount. That's why we've used padding plus the border width in this situation.

🎉📚Get the #15DaysOfCSS e-book📚🎉

These #15DaysOfCSS posts will be compiled into an e-book — pre-order now!

Email is great, and blogs are awesome, but it can be challenging to use them as a reference in the future. We'll be assembling this month's work into an e-book.

Pre-order the book

🖥 Today’s CodePen example

Today’s CodePen example maps out the code described in this email.

View today's CodePen example

👩🏽‍💻 Challenge: Order the dumplings! 🥟

👩🏽‍💻 In today’s challenge, make a navbar with a very clear call to action to ORDER DUMPLINGS. Or maybe there were other choices. We got distracted.

Take Today's CodePen Challenge

📚 More information and examples

📚 CSS Tricks: A complete guide to Flexbox

📚 CSS Tricks: A complete guide to calc()

📚 MDN: calc()

📽 LinkedIn Learning: HTML and CSS Creating Navigation Bars (subscription required)

📽 Flexbox and Grid version 2 at Frontend Masters (subscription required)