Skip to the content

CSS project 2a, turning a list of links into a horizontal menu

Last updated: By Lukas BrandtLesson 17 of 214 min read

At the end of the last lesson the header band looked right, but the menu was still a bulleted list running down the page. This lesson turns it into a row of links. The HTML does not change. Here is the part we are working on:

<nav class="main-nav" aria-label="Main">
  <ul class="menu">
    <li><a href="index.html" aria-current="page">Home</a></li>
    <li><a href="guides.html">Guides</a></li>
    <li><a href="tools.html">Tools</a></li>
    <li><a href="contact.html">Contact</a></li>
  </ul>
</nav>

Keeping the menu as a list is the right call even though it will not look like one. Screen readers announce a list along with the number of items in it, which tells a listener how big the menu is before they hear any of it.

Five steps to a row

Add these two rules to style.css, below the ones you already have:

.menu {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.menu a {
  display: block;
  padding: 8px 12px;
  border-radius: 4px;
}

Here is what each part does, in the order you would notice it:

  1. Remove the bullets. list-style: none hides the markers. The list is still a list in the HTML; it just stops looking like one.
  2. Remove the default spacing. Browsers give a <ul> a margin above and below and about 40 pixels of padding on the left, to make room for the bullets. margin: 0 and padding: 0 remove both, so the menu no longer sits indented under the logo.
  3. Line the items up. display: flex turns the <ul> into a flex container. Its direct children, the four <li> elements, become flex items and sit side by side in a row. Note that the flex items are the list items, not the links inside them.
  4. Space them out. gap: 8px puts 8 pixels between neighboring items and nothing before the first or after the last. flex-wrap: wrap lets the row break onto a second line when the window is too narrow, instead of squeezing the links or running off the edge.
  5. Give each link a real box. A link is inline by default, and vertical padding on an inline box does not push the lines around it away (the display lesson covers inline and block boxes). display: block makes the link fill its list item, so the padding counts and the whole padded area is clickable. That bigger target matters on a phone, where a finger is much less precise than a mouse pointer. The border-radius has nothing visible to round yet; it comes into play when the hover background arrives in lesson 19.

Save, reload, and the four links now sit in a row under the site name, still underlined, still white.

The older way: inline-block

Before flexbox worked everywhere, menus were built with floats (covered in the float lesson) or with inline-block. The inline-block version looks simple:

/* older approach, shown for reference */
.menu {
  list-style: none;
  margin: 0;
  padding: 0;
}

.menu li {
  display: inline-block;
  margin-right: 8px;
}

An inline-block element sits in a line of text like a word, but accepts width, height and vertical padding like a block. Set the list items to inline-block and they flow along one line.

The catch is that they really do behave like words. The line break and indentation between </li> and the next <li> in your HTML count as a space, so the browser adds a gap of roughly a space character between items on top of your margin. The gap changes with the font size, and it shows up in measurements as a few mystery pixels. The usual workarounds were ugly: deleting the whitespace from the HTML, writing <!-- --> comments between items, or setting font-size: 0 on the list and restoring it on the items. Items also line up on the text baseline, which can leave them slightly out of line when they hold different content.

A flex container ignores whitespace between its items and stretches them to the same height, so none of that comes up. The inline-block approach does have one thing going for it, which the next lesson uses: since the items are inline, text-align on the list moves them around.

Parts links can earn us a commission. As an Amazon Associate I earn from qualifying purchases.

Try it

Paste the project HTML into the online HTML editor, swap the <link> line for a <style> element holding everything in style.css so far, and add the two new rules. Then change gap to 24px, and remove flex-wrap: wrap and narrow the window to see the links squeeze together instead of wrapping.