A Very Simple Step-by-Step Explanation of Creating a Drop-down Menu via HTML/CSS

So many tutorials for creating drop-down menus using HTML/CSS, but none of them this simple. This covers the basics, going one step at a time, of creating a drop-down menu that holds navigation links, like "Page Two of this Web Site".

1 - Start with a simple and valid HTML file.

An HTML (Hyper-Text Markup Language) file is a file designed to be served by web-servers (like Apache or IIS) to web-browsers (like Firefox and Chrome). You can save your HTML file as "index.html" or "menu.html" or something similar, and either serve it to your web-browser by placing it on a web-server, or by just using your web-browser's File / Open File option to open the file.

<!DOCTYPE html>
<html>
  <body>
    <p>This is valid HTML.</p>
  </body>
</html> 

Really, a "more valid" HTML file would look like this (adding what is in highlight):

<!DOCTYPE html>
<html>
  <head>
  </head>
  
  <body>
    <p>This is valid HTML.</p>
  </body>
</html> 

And an even better HTML file would be:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>A Very Simple Step-by-Step Explanation of Creating a Drop-own Menu via HTML/CSS</title>
    <link href="/style.css" rel="stylesheet" type="text/css" media="all">
  </head>

  <body>
    <p>This is valid HTML.</p>
  </body>
</html> 

The <meta> and other items in the <head> section aren't strictly necessary, and you don't really need to know at this point what they do, but it makes your HTML file ... better.

5 - Use CSS to Decorate HTML Elements

Again, there are three places we could put these CSS rules to create a border around the <nav> section. But like I did above, I'll put the rules in the <style> section in the <head> section.

...
     <style>
      nav {
        border: solid 1px red;
      }
      nav a {
        display: block;
      }
    </style>
...

The above code should result in something like this:

That red rectangle marks the boundaries of the <nav>...</nav> block. If we add in another block element, another <div>, with similar styling, you can see how it stacks below the previous <nav</> block:

  <body>
    <p>This is valid HTML.</p>

    <nav>
      <a href="first-page.html">First Page of Web Site</a>
      <a href="second-page.html">Second Page of Web Site</a>
      <a href="third-page.html">Third Page of Web Site</a>
    </nav>
    
    <div style="border:dashed 2px blue;">
      <p>This is another "div".</p>
    </div>
    
  </body>

The above code should result in something like this:

As you can see, it's perfectly fine to mix your methods of adding CSS. The <nav> section's CSS is in the <style> section in the <head> section, whereas the CSS for the new <div> we just added is inline with that <div> element.

The CSS rule that is "closest" to the element to which it applies takes precedence. Suppose we had a CSS rule for a border in all three locations, with the inline rule specifying green, the in-file rule specifying red, and the external stylesheet specifying blue. The "closest" rule would be the inline rule, so the border would be green.

These rules specify a border in two cases: a solid red border for all instances of <nav> (of which there should only ever be one instance, but that might not always be the case for one reason or another), and a dashed blue one for just the single instance of <div>, because the CSS rule is inline with that one element.

If we add a second, third, and fourth <div> without inline CSS rules, they won't get the blue border:

  <body>
    <p>This is valid HTML.</p>

    <nav>
      <a href="first-page.html">First Page of Web Site</a>
      <a href="second-page.html">Second Page of Web Site</a>
      <a href="third-page.html">Third Page of Web Site</a>
    </nav>
    
    <div style="border:dashed 2px blue;">
      <p>This is another "div".</p>
    </div>
    
    <div>
      <p>This is a second "div".</p>
    </div>
    
    <div>
      <p>This is a third "div".</p>
    </div>
    
    <div>
      <p>This is a fourth "div".</p>
    </div>

  </body>

The above code should result in something like this:

If you want the blue border around all <div>s, you could move the CSS rule out of the inline position, into the file's <head>/<style> section:

...
     <style>
      nav {
        border: solid 1px red;
      }
      nav a {
        display: block;
      }
      div {
        border: dashed 2px blue;
      }
    </style>

...
    <div style="border:dashed 2px blue;">
      <p>This is another "div".</p>
    </div>
    
    <div>
      <p>This is a second "div".</p>
    </div>
    
    <div>
      <p>This is a third "div".</p>
    </div>
    
    <div>
      <p>This is a fourth "div".</p>
    </div>

...

If you don't want the rule to apply to all <div>s, you can break your <div>s (or any element) up into different kinds, or classes, and just apply the rule to a specific class of <div>. For example, say we want a yellow border around div 1, a blue border around div 2 and 4, and no border around div 3. We can name the red class "yellowbox", the blue class "feelin-blue", and the no-border class "no-border". We assign these class names to the divs as needed, and then create the CSS rules for these classes. Classes in the CSS rules are indicated by a period at the start of their names.

...
     <style>
      nav {
        border: solid 1px red;
      }
      nav a {
        display: block;
      }
      div.feelin-blue {
        border: dashed 2px blue;
      }
      .yellowbox {
        border: solid: 4px yellow;
      }
      .no-border {
        border: none;
      }
    </style>

...
    <div class=yellowbox">
      <p>This is another "div".</p>
    </div>
    
    <div class="feelin-blue">
      <p>This is a second "div".</p>
    </div>
    
    <div class="no-border">
      <p>This is a third "div".</p>
    </div>
    
    <div class="feelin-blue">
      <p>This is a fourth "div".</p>
    </div>

...

Strictly speaking, we don't need the "no-border" class, since the default is to not have a border, but having this class ensures that this class/kind of div does not "inherit" a border from some other "parental" HTML/CSS mechanism.

The above code should result in something like this:

This should give you enough understanding of CSS rules to comprehend what we're doing in this tutorial.

We don't need the <div>s we just added, nor the "This is valid HTML" line, nor the colored borders; let's remove those, and get our file back to where it looks like this:

   <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>A Very Simple Step-by-Step Explanation of Creating a Drop-down Menu via HTML/CSS</title>
    <link href="/style.css" rel="stylesheet" type="text/css" media="all">
    <style>
      nav a {
        display: block;
      }
    </style>
  </head>

  <body>
    <nav>
      <a href="first-page.html">First Page of Web Site</a>
      <a href="second-page.html">Second Page of Web Site</a>
      <a href="third-page.html">Third Page of Web Site</a>
    </nav>
    
  </body>

The result of this code should be just the links, in block-display format:

6 - Add a "Menu" Button

Let's put a "Menu" button on the page.

I understand it's good practice to always include the button type, but I'm not sure how necessary that is. There are three types: "button", submit", and "reset". These buttons are usually used with "forms" (and Javascript/etc), which we're not doing, so the basic "button" type is all we need.

  <body>

    <nav>
      <button type="button">Menu</button>
      <a href="first-page.html">First Page of Web Site</a>
      <a href="second-page.html">Second Page of Web Site</a>
      <a href="third-page.html">Third Page of Web Site</a>
    </nav>

  </body>

The above code should result in something like this:

The problem with this is that if we have more links, and we have to scroll to get to the bottom of the list, the "Menu" button will scroll off the top of the screen. Let's add some more links to demonstrate this. While we're doing that, let's add some more content to the web page:

    <nav>
      <button type="button">Menu</button>
      <a href="first-page.html">First Page of Web Site</a>
      <a href="second-page.html">Second Page of Web Site</a>
      <a href="third-page.html">Third Page of Web Site</a>
      <a href="p4.html">Fourth Page of Web Site</a>
      <a href="p5.html">Fifth Page of Web Site</a>
      <a href="p6.html">Sixth Page of Web Site</a>
      <a href="p7.html">Seventh Page of Web Site</a>
      <a href="p8.html">Eighth Page of Web Site</a>
      <a href="p9.html">Ninth Page of Web Site</a>
      <a href="p10.html">Tenth Page of Web Site</a>
      <a href="p11.html">Eleventh Page of Web Site</a>
      <a href="p12.html">Twelfth Page of Web Site</a>
    </nav>
    
    <p>Here's a paragraph of text. It is utterly meaningless, except as
    a visual aid to see where the rest of the HTML content of your web page
    goes. Had this been an actual paragraph of real text, you would be more
    entertained or informed by it than you are. As it is, this is just blah
    blah blah, yada yada yada. Bloop-de-doo, skiddely-pop. And dat's da tooth!</p>

Try scrolling in the window to see the "Menu" button disappear off the top of the screen as you scroll.

Fig 3.2

There's a solution to this that works great on a large-ish desktop monitor, but it doesn't work so well on a small-screen, like a mobile phone. But we're going to use this solution anyway, because it seems to be "the proper" way to do it, and until the web-development industry provides a better solution, this is probably the best one we have.

The solution is to use CSS to tell the "Menu" button to remain in a "fixed" position on the screen. We'll give the "Menu" button an ID so we can target it specifically, and then in the CSS rules we'll target that ID with the positioning information (a # indicates we're targeting a specifically-ID'd element). Here's the code to do that:

    <style>
      nav a {
        display: block;
      }
      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
      }
    </style>
...
    <nav>
      <button id="menu_button" type="button">Menu</button>
...

Now try scrolling in the window again; the "Menu" button should remain in the top left corner at all times (unless you're on a small screen).

Fig. 3.4

However, the "Menu" button over-rides any text that scrolls underneath it. There are a few things we could do to help prevent that, such as making the "Menu" button smaller, or by moving the rest of the text to the right enough to miss the button.

We could make the button smaller just by replacing the word "Menu" with those three lines called a "hamburger" - ≡ (I can generate that on my Debian GNU/Linux keyboard by pressing Ctrl-Shift-U, then letting go of those keys and typing 2261, then pressing ENTER.) Or an actual hamburger icon - 🍔 (Ctrl-Shift-U, 1f354, ENTER).

...
      <button type="button">Menu</button>
      <button type="button">🍔</button>
...
Fig. 3.6

But that doesn't really work all that well. Maybe a combination of using a smaller icon, and moving the text to the right? You may have to experiment with the number of pixels to pad on the left-hand side of the "nav" section, to get it to look right.

...
    <style>

      nav {
        padding-left: 40px;
      }
      nav a {
        display: block;
      }
      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
      }
...
      <button type="button">🍔</button>
      <button type="button">≡</button>
...
Fig. 3.8

That works pretty well for the navigational links, but not so much for the rest of the body of the document, which you'll notice if you scroll upward until the added text reaches the menu button. Let's move that padding from the nav section to the body section.

...
    <style>

      body {
        padding-left: 40px;
      }
      nav {
        padding-left: 40px;
      }
      nav a {
        display: block;
      }
...
Fig. 6.8

Ah, that works well.

Now, we need to hide the contents of the menu when we don't need them.

7 - Hide the Menu Contents When Not Being Used

By default, we'll hide the links. Then when we hover over the button, we'll unhide the links.

Hiding the links is easy. We just "target" the whole structure that holds those links, the <nav> section, and use CSS to set the display of that section to "none". We just deleted a <nav> section from our <head>/<style> section, so let's put that back, and add the CSS to set that section's display property to "none":

...
    <style>
      body {
        padding-left: 40px;
      }
      nav {
        display: none;
      }
      nav a {
        display: block;
      }
...

Here's the result:

Fig. 7.2

Oops. That also hides the "hamburger" menu button. This is because the hamburger is within the <nav> section, and when we hide that section, we hide everything in it.

But we don't want to hide the hamburger, just the links. So instead of hiding the <nav> section, let's create a new <div> section within the <nav> section, and hide that new <div> section.

We can create a class (classification, genre, type) of "div", and specify our changes to this class of divs. We'd do that like so:

...
    <style>
      body {
        padding-left: 40px;
      }
      .nav_links {
        display: none;
      }
      nav a {
        display: block;
      }
      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
      }
...
      <nav>
        <button type="button">Menu</button>
        <div class="nav_links">
          <a href="first-page.html">First Page of Web Site</a>
          <a href="second-page.html">Second Page of Web Site</a>
          <a href="third-page.html">Third Page of Web Site</a>
          <a href="p4.html">Fourth Page of Web Site</a>
          <a href="p5.html">Fifth Page of Web Site</a>
          <a href="p6.html">Sixth Page of Web Site</a>
          <a href="p7.html">Seventh Page of Web Site</a>
          <a href="p8.html">Eighth Page of Web Site</a>
          <a href="p9.html">Ninth Page of Web Site</a>
          <a href="p10.html">Tenth Page of Web Site</a>
          <a href="p11.html">Eleventh Page of Web Site</a>
          <a href="p12.html">Twelfth Page of Web Site</a>
        <div>
      </nav>

Or, perhaps better, since there will only be one set of links that we want to manipulate at a time, we can identify this particular div by an id, which would look like this:

...
    <style>
      body {
        padding-left: 40px;
      }
      #nav_links {
        display: none;
      }
      nav a {
        display: block;
      }
      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
      }
...
      <nav>
        <button type="button">Menu</button>
        <div id="nav_links">
          <a href="first-page.html">First Page of Web Site</a>
          <a href="second-page.html">Second Page of Web Site</a>
          <a href="third-page.html">Third Page of Web Site</a>
          <a href="p4.html">Fourth Page of Web Site</a>
          <a href="p5.html">Fifth Page of Web Site</a>
          <a href="p6.html">Sixth Page of Web Site</a>
          <a href="p7.html">Seventh Page of Web Site</a>
          <a href="p8.html">Eighth Page of Web Site</a>
          <a href="p9.html">Ninth Page of Web Site</a>
          <a href="p10.html">Tenth Page of Web Site</a>
          <a href="p11.html">Eleventh Page of Web Site</a>
          <a href="p12.html">Twelfth Page of Web Site</a>
        <div>
      </nav>

Notice that a class is identified with a dot ("."), and an id is identified with a splat ("#"). Also notice that an HTML document can have only one element id'd by a particular name. Whereas you can have several sections of a document classified as a "nav_links" class, you can only have one section of a document id'd as a "nav_links" id. If you have two menu areas, say, one at the top of the page and another down the side, you might want to class both of them as a "class", so that you can make a single change to the class, say, the background color. But if you want to target only one of those menus to be manipulated, so, to hide or unhide it, but to leave the second menu always showing, you'd want to "id" it instead of "class" it.

Using the "id" method, here's the result:

Fig. 7.4

Now we need to unhide the menu contents when we hover over the button.

8 - Reveal the Menu Contents When Needed

Of course, having the menu contents hidden doesn't make for a very good menu. We need to unhide those comments when the user hovers their mouse pointer over the menu button. Again, this can be done with CSS rules. We'll make a rule that applies to the #menu_button, using the :hover feature, which watches for a mouse hovering over that element to which it is applied. What the CSS code below does is to watch for the mouse to hover over the #menu_button element, and when that happens, to do something to the nav_links CSS rules. We had already used CSS on nav_links to set the display to "none"; now the thing we'll do when the mouse hovers over the menu button is to turn the display to "block". Because the ":hover" feature is picky, we'll change the order of things up a bit.

The following should work:

...
    <style>
      body {
        padding-left: 40px;
      }

      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
      }

      #menu_button:hover + #nav_links {
        display:block;
      }

      #nav_links {
        display: none;
      }
      nav a {
        display: block;
      }
    </style>
...    

But when we try it ...

Fig. 8.1

... it at first looks like it works, but you can't move your mouse to one of the links without the links disappearing. This is because when you move your mouse to a link, you move it off the menu button, and then the menu button loses the hover focus, and the #nav_links once again reset their display to "none".

So instead of targeting #menu_button, we can target the parent container, nav. When #nav_links are hidden, the only hoverable element visible to the document/mouse is the menu button, but when the #nav_links are not hidden, the button and those nav_links are hoverable. So when just the menu button is hoverable (because it's part of the nav container, which is hoverable), you can move your mouse to it, and the nav_links become visible and hoverable, so you can move your mouse from the menu button to the nav links. Below shows the change we need to make. Also note the removal of the plus sign ("+"); this again has to do with the pickiness of "hover:" and the placement of things; understanding this will require research on your part.

...
    <style>
      body {
        padding-left: 40px;
      }
      
      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
      }
      

      #menu_button:hover + #nav_links {
      nav:hover #nav_links {
        display:block;
      }

      #nav_links {
        display: none;
      }

      nav a {
        display: block;
      }

    </style>
...    

And here's the result:

Fig. 8.2

Wha-a-a-ahhh?! It still doesn't work!

It might work for you, depending on your screen size, but for me, because the body is padded on the left-hand side by 40 px, there's a small gap between the right-side of the menu button and the left side of the <nav> section (which is in the body and not positioned out of it like the menu button is positioned out of it). We can fix this by moving the <nav> section to the outside of the body, to the top, left corner, over-laying the menu button. Here's how we do that:

...
    <style>
      body {
        padding-left: 40px;
      }

      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
      }

      nav:hover .nav_links {
        display:block;
      }
      
      #nav_links {
        display: none;
        position: fixed; top: 0; left: 0px;
      }
      
      nav a {
        display: block;
      }
    </style>
...    

And the result:

Fig. 8.4

Well, that moved the text, but now it's jumbled with the underlying text. Since we don't need to see the underlying text while the menu is showing, we can just create a background color for the links:

...
      #nav_links {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        background-color: ivory;
      }
...

Results:

Fig. 8.6

But, because the nav links section is larger than this window, the links disappear off the window, and we can't get to them. The fix is to limit the height of the nav links section so that it's smaller than the window height, and to set any text overflow to "auto" which will turn on a scrollbar if needed.

...
      #nav_links {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        background-color: ivory;
        height: 80px;
        overflow: auto;
      }
...

Results:

Fig. 8.8

That's pretty much got it. If for some reason you wanted the menu button to stay on top of the nav links when they appear, you can just set its z-index to a number high enough to be higher than the other stacked elements at that location. 2 would probably do it, but I'll overkill with a 7, because my brain is getting tired and I don't want to put the thought into figuring out how many elements might be stacked at this location.

      #menu_button {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 7;
      }

Results:

Fig. 8.9

However, now the menu button clobbers some of the text of the nav_links; we can fix that by padding the nav_links:

...
      #nav_links {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        background-color: ivory;
        height: 80px;
        overflow: auto;
        padding-top: 20px;
      }
...

Conclusion

There's a lot more that can be done with this, but if you can wrap your head around these basics, the hundreds of other tutorials can take you to those places. This was just so you can get the minimal basics in your head, one step at a time.