Complete CSS Course Module 8: Intermediate: Flexbox Layout

Flexbox Project: Build a Responsive Navigation Bar

  • Beginner
  • Lesson 8.4
  • 6 min read
  • 34 of 77 in the course

In short

In this flexbox project you build a responsive navigation bar step by step: a logo, links and a button, with hover states and wrapping on small screens. The full final code is at the end. What we are going to build A navigation bar (or navbar) is the menu at the top of almost every website.

In this flexbox project you build a responsive navigation bar step by step: a logo, links and a button, with hover states and wrapping on small screens. The full final code is at the end.

What we are going to build

A navigation bar (or navbar) is the menu at the top of almost every website. Ours will have three parts:

  • A logo on the left.
  • A list of links in the middle.
  • A "Sign up" button on the right.

On a wide screen, all three sit on one line. On a small screen, the links move to their own line below the logo and button, and they can wrap if needed. We will use only Flexbox, no JavaScript and no media queries.

Create a file called index.html and follow along. Each step adds a little more CSS.

Step 1: Write the HTML structure

Good HTML first. We use a <header> for the top area, a <nav> for the main menu, and a list for the links, because a menu is a list of choices.

<header class="navbar">
  <a href="/" class="logo">Code<span>Craft</span></a>

  <nav class="nav" aria-label="Main">
    <ul class="nav-links">
      <li><a href="/" aria-current="page">Home</a></li>
      <li><a href="/courses">Courses</a></li>
      <li><a href="/projects">Projects</a></li>
      <li><a href="/pricing">Pricing</a></li>
      <li><a href="/blog">Blog</a></li>
    </ul>
  </nav>

  <a href="/signup" class="btn">Sign up</a>
</header>

Without CSS, you see a link, a bulleted list and another link, stacked vertically. The aria-current="page" attribute tells screen readers which page you are on, and we will style it too.

Step 2: Put everything on one line with flexbox

Now we make the header a flex container. The three children (logo, nav and button) become flex items.

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
}

.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 24px;
  background: #ffffff;
  border-bottom: 1px solid #e5e7eb;
}

The logo, menu and button now sit in one row. align-items: center lines them up vertically in the middle, and justify-content: space-between pushes the logo to the left and the button to the right, with the menu in between.

Next we make the list horizontal. The <ul> becomes its own flex container, and we remove the bullets and default spacing.

.logo {
  font-size: 1.4rem;
  font-weight: 800;
  color: #1f2937;
  text-decoration: none;
}

.logo span {
  color: #2563eb;
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-links a {
  display: block;
  padding: 8px 14px;
  border-radius: 8px;
  color: #4b5563;
  text-decoration: none;
  font-weight: 500;
}

The links now sit in a row with a little space between them. We gave each link display: block and padding, so the clickable area is bigger than the text. This is much easier to tap on a phone.

Tip: Put the padding on the <a>, not on the <li>. Then the whole padded area is clickable, not only the words.

Step 4: Add hover and focus states

Users need feedback when they point at a link or reach it with the keyboard. We also highlight the current page.

.nav-links a:hover {
  background: #f3f4f6;
  color: #1f2937;
}

.nav-links a:focus-visible,
.btn:focus-visible {
  outline: 3px solid #f59e0b;
  outline-offset: 2px;
}

.nav-links a[aria-current="page"] {
  color: #2563eb;
  background: #eff6ff;
}

.btn {
  flex-shrink: 0;
  background: #2563eb;
  color: white;
  padding: 10px 18px;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 600;
}

.btn:hover {
  background: #1d4ed8;
}

Hovering a link gives it a soft grey background. Tabbing through the menu shows an orange focus ring. The "Home" link is blue because the attribute selector matches aria-current="page". The button gets flex-shrink: 0 so it never gets squeezed.

Common mistake: Styling only :hover. Keyboard and touch users never hover. Always add a visible :focus-visible style too.

Step 5: Make the navbar wrap on small screens

When the screen gets narrow, there isn't room for everything on one line. We don't need a media query for this. We let the header wrap, and we let the nav grow to fill the space it gets:

.navbar {
  flex-wrap: wrap;
  gap: 12px 16px;
}

.nav {
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
}

.nav-links {
  justify-content: center;
}

Now the browser fits the logo, nav and button on one line when there is room. When there isn't, the nav and button wrap onto a new line, and the links inside the nav wrap too, because .nav-links has flex-wrap: wrap. Nothing ever overflows the screen. The nav is also a flex container with justify-content: center, so the links stay centered in the space they get.

Note: On very complex sites, a "hamburger" menu that opens and closes with a button is common on phones. That needs JavaScript or extra HTML, so it is outside this project. A wrapping menu is simple, accessible and works great for five or six links.

The full final code

Here is the complete page. Copy it into index.html, open it in your browser, and resize the window to watch the navbar wrap.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Responsive Navbar</title>
  <style>
    * {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      font-family: system-ui, sans-serif;
      background: #f9fafb;
    }

    .navbar {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      justify-content: space-between;
      gap: 12px 16px;
      padding: 12px 24px;
      background: #ffffff;
      border-bottom: 1px solid #e5e7eb;
    }

    .logo {
      font-size: 1.4rem;
      font-weight: 800;
      color: #1f2937;
      text-decoration: none;
    }

    .logo span {
      color: #2563eb;
    }

    .nav {
      flex: 1 1 auto;
      display: flex;
      justify-content: center;
    }

    .nav-links {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 4px;
      list-style: none;
      margin: 0;
      padding: 0;
    }

    .nav-links a {
      display: block;
      padding: 8px 14px;
      border-radius: 8px;
      color: #4b5563;
      text-decoration: none;
      font-weight: 500;
    }

    .nav-links a:hover {
      background: #f3f4f6;
      color: #1f2937;
    }

    .nav-links a[aria-current="page"] {
      color: #2563eb;
      background: #eff6ff;
    }

    .nav-links a:focus-visible,
    .btn:focus-visible {
      outline: 3px solid #f59e0b;
      outline-offset: 2px;
    }

    .btn {
      flex-shrink: 0;
      background: #2563eb;
      color: white;
      padding: 10px 18px;
      border-radius: 8px;
      text-decoration: none;
      font-weight: 600;
    }

    .btn:hover {
      background: #1d4ed8;
    }

    main {
      max-width: 800px;
      margin: 40px auto;
      padding: 0 24px;
    }
  </style>
</head>
<body>
  <header class="navbar">
    <a href="/" class="logo">Code<span>Craft</span></a>

    <nav class="nav" aria-label="Main">
      <ul class="nav-links">
        <li><a href="/" aria-current="page">Home</a></li>
        <li><a href="/courses">Courses</a></li>
        <li><a href="/projects">Projects</a></li>
        <li><a href="/pricing">Pricing</a></li>
        <li><a href="/blog">Blog</a></li>
      </ul>
    </nav>

    <a href="/signup" class="btn">Sign up</a>
  </header>

  <main>
    <h1>Learn to code, one step at a time</h1>
    <p>Resize your browser window to see the navigation bar wrap neatly on small screens.</p>
  </main>
</body>
</html>

On a laptop, you see the logo on the left, five links in the middle and a blue "Sign up" button on the right. On a phone, the links and the button move to a second line and wrap as needed, and nothing is cut off or causes a sideways scroll.

Practice exercise

Improve your navbar in three ways: make it stick to the top of the page while scrolling, add a small "New" badge next to the "Projects" link, and add a second, outlined "Log in" button next to "Sign up".

Hint: use position: sticky; top: 0; on .navbar. Wrap both buttons in a <div> that is itself a flex container with a gap, so they stay together when the navbar wraps.

Key takeaways

  • A navbar is a perfect flexbox job: one row of items with alignment and spacing.
  • justify-content: space-between and align-items: center place the logo, menu and button.
  • Nested flex containers (the <ul> inside the header) let you lay out the links separately.
  • Big padding on links, plus :hover and :focus-visible states, make the menu easy to use.
  • flex-wrap: wrap on both the header and the list keeps the navbar working on small screens.