Drop-Down Navigation with CSS only

Note: This is an update of an old popular post.

In this tutorial I show how to make drop-down navigation using CSS only. This tutorial starts with the code that I presented in the Simple Horizontal Navigation tutorial.

See the Pen Drop Down Navigation (CSS Only) by Lisa Catalano (@lisa_c) on CodePen.

Preferred Learning Method?

Code Snippets
If you just want the code and don’t need a description.

Watching a Video
If you like being shown how something works with some explanation watch the video.

Reading a description
If you learn better by reading, I am including the transcript of the video with corresponding code snippets embedded.

Code Only

See the Pen Drop Down Navigation (CSS Only) by Lisa Catalano (@lisa_c) on CodePen.

See the Pen Drop Down Navigation (CSS Only) by Lisa Catalano (@lisa_c) on CodePen.


If you would like to follow along with the video:

Starting code here
Ending code here


In this video I’m going to talk about how to create a Drop Down navigation menu using CSS only.

Before I start I should mention that this technique is not great for small screens or mobile websites. I’ll still approach this from a mobile-first perspective, but the solution I present is not perfect for small screens. I’ll tackle how to make it better in a future video.

I’m starting with code I presented in my Simple Horizontal Navigation tutorial. You might want to watch that video first if you don’t understand all of the code I am starting with.

If you want to follow along you can grab the code from my GitHub repository named “dropdown”. I’ll post a link.

I’m using the Brackets code editor so I can see the Live Preview as I work. You can see that this is a simple navigation that has different colors on the active item and also when you hover.

When I created this navigation I used a mobile-first approach and I started with a simple vertical navigation menu that changes to horizontal on larger screens.

HTML Changes

To add sub-menus, you simply need to add a nested unordered list inside the top menu item.

So if I want a sub-menu under Tutorials, I can go to that li tag and inside of it, insert another ul tag and some li tags for each menu item with a link inside. The syntax is just like the top level menu, just nested inside. I’m adding 3 menu items with some Emmet shortcuts.

I will also create a sub-menu for Newsletters.

            <li><a href="#">Tutorial #1</a></li>
            <li><a href="#">Tutorial #2</a></li>
            <li><a href="#">Tutorial #3</a></li>

Now this looks terrible on both the small screen and larger screen.

I’ll work on the small screen first.

CSS Changes for the Small Screen

The reason this is unreadable is because the sub-menus are rendering on top of the main menu. That is because there is a height property under .nav li. The list items with sub-menus are only given 40px which is not enough to contain the sub-menus.

If I remove it, the height will default to auto instead which means the browser will calculate the height needed based on its contents. Now we can see all the menu items.

The new problem is that the font size for the sub menu is larger than the top menu.

That is because I specified the font-size with the em unit. Ems are calculated based on the font-size of the parent. And when you have nested elements, that can cause a multiply effect. In this case, the sub menus are getting a font-size that is 1.2 times as large as the main menu.

So using ems can be frustrating when you are nesting things that specify a font-size.

There is another unit of measure called rems which stands for “root ems” and will always base its value on the root element instead of the parent. That means you avoid the cascade and the multiplier effect.

I will change the unit of measure to rem.

Now all the items are the same size.

However, rem does not work in IE8 and below so if you need to support that you need a different solution. You could use px instead.

I actually want the sub-menus to be smaller text, to help distinguish them from the top menus. So I am going to create a new selector for the sub menu li tags and then I can specify a different font-size.

If I specify the li that is inside of another li it will target the nested lists items only.

When I set it to 1em, you can see that the size is consistent.

/* Sub Menu code */
.nav li li {
  font-size: 1em;

But I’ll set this to .8em so that it is a little smaller.

I don’t like the text centered with this longer menu. This is coming from this text-align: center here on the unordered list. I want to leave this on the ul tag because I want this menu centered on big screens, but I will make the li tags have left alignment.

So I’ll add text-align: left to .nav li.

Then I need to add some padding so it doesn’t hug the left. If I add that to the list item, the border gets mess up, so I will move that line to the anchor tags.

(.nav a)

padding-left: 15px;

The final problem on the small screen menu is that the border is not showing up in between all the lines.

I have the border at the bottom of the list items, but since the bottom of the Tutorials or Newsletter list item is underneath the sub menu, the main menu list item doesn’t get the border.

Instead I’ll move that line of code down to the a tag also.

Now the small menu looks better.

CSS Down-Down Navigation on Larger Screens

So finally, I can tackle the main topic of this tutorial and that is how to make the sub-menus only show on hover on larger screens. That actually won’t take much time.

This next part of the code is happening inside of the media query. I am using a breakpoint of 650px because that is how much space the menu needs to be displayed horizontally on one line based on the width of the menu items. You can use ems or whatever breakpoint makes sense for your design.

This code inside the media query will only happen on screens that have 650 pixels or more.

The first thing I’ll do is set the positioning of the sub-menu to absolute, so that the sub menu is taken out of the flow of the document and doesn’t take up height anymore on the main nav bar.

I targeted the ul tags inside of li so that this only applies to the nested menus.

Now I want to hide these sub-menus. I can set the display property to none so the sub-menu does not display.

.nav li ul {
  position: absolute;
  display: none;

It is a little hard to see, but the bottom border is peeking out from the main navigation bar, so I want to turn that off on larger screens.

.nav a {
  border-bottom: none;

You can see the difference when I toggle this line on and off.

Next we want to display the menu when we hover on the parent menu. So we can target hover on the parent li and then the unordered list.

.nav li:hover ul {
  display: block;

The sub-menu is displaying on hover, but it is a horizontal menu. That might be what you want. If so, you could stop and style it so it is placed where you want and looks better.

But I want a vertical navigation.

The reason it is horizontal is because I am using display: inline-block on all li tags inside of .nav. That was to make the main navigation horizontal.

If I want this sub navigation to be vertical I need to target the sub li tags and display them as block;

.nav li ul li {
  display: block;

The width of sub-menu is a little short. That is because li’s have a width of 130px, but the background color is coming from the ul tag, which does not have a width set and therefore defaults to auto.

I want the sub-menu ul tags to have the same width as the parent li. If I say width: inherit it inherits the width of its parent, or the list item.

Now everything lines up nicely.

I don’t like that the text is now left-aligned. Remember I fixed that for the small screen.

If I add this code in the media query

  .nav ul  li {
    text-align: center;

It still doesn’t look like the items are centered aligned because of the left-padding I added for the small screen menu. I’m going to ignore that briefly and will fix that in a moment.

Now all menu items are aligned to the center, including the sub-menu. It is not very noticable when all my text is the same width, but if I change one line, you can see it.

I want to center the top menu only. It is easy to target the child menus by adding more selectors, but I can’t do that on the top menu. I could add a class, or I can target the direct descendants of the nav class by using the child selector, which is the >.

See how when I add the child selector, only the top navigation is centered. Well, centered plus 15px of padding on the left.

I can use the child selector again to remove that padding.

  .nav > ul > li > a {
    padding-left: 0;

When I’m using this child selector it will not target the sub-menu anchor tags because they are not direct descendents underneath the .nav div.

The padding-left of 15px will remain on the child menus.

Of course the styling of your menus depends on your actual content and design. You may not like the styles I’m using here and you should adjust to your own preference and design.

The main concept is the hidden menu that displays on hover. And that part of the code is right here. We hide the nested menu in the normal state, and we display it on hover. If you want it displayed vertically, you want to set display to block on the menu items.

The important thing to notice in this code is the selector. I am selecting ul tags inside of li tags. That means it is only targeting the nested sub-menus. It gets a little confusing looking at the different li and ul tags, so make sure you understand that. You could add classes to the sub menus to make things more clear if you want.

Browser Support

This will work in all browsers except IE8. That is because IE8 doesn’t recognize media queries. So we need to put all the code from the media query in the IE8 and below style sheet.

If you are using a pre-processor, you can avoid maintaining duplicate code, by putting the code in a separate file, and importing it into both the main style sheet and the IE style sheet.

So things are looking decent in both sizes.

What’s Next

There are more things to do to this to make it better. As I mentioned the small screen shouldn’t be displayed at the top of the page as is. On a phone, it would probably take up the entire screen. Also, some indicators that there are hidden menus would be useful here so the user knows they can hover to see more. Some transitioning on the drop-downs menus would be nice as well.

I plan on tackling these concepts in future tutorials coming soon.

Thanks for watching, please let me know if you have any questions.

44 thoughts on “Drop-Down Navigation with CSS only

  1. This is the same Nav code I use for my website but it does not work in Internet Explorer. I’ve tried mine & yours and neither work on my mac IE or windows IE.

    Have you had any problems since this post?


    1. It does work for me in IE. Right now I have IE9, but I remember testing in IE8 before I posted.

      What exactly is happening when you run this in IE.

  2. I just tested a few computers in my office. It definitely works in IE9 here as well, but not in the older version of IE. In IE6 the drop downs do not appear at all – you only see the main menu.

    Now I know I can tell people to either upgrade their IE or just not use it like most of the population. Google Chrome is such a better brower.

    I have found codes that do work in IE6 and after looking at them for a while I can’t pin point what makes them work and not mine. ??

    1. Hi Fran,

      I only have Safari 5.7.1 since I’m on Windows, so I can’t test on latest versions of browser. But it is working fine for me. I also tried on my iPhone’s version of Safari and everything is lined up ok there.

      Are you talking about the Demo page, or just trying to use the snippet of code I have here?

      My best guess is that there is a problem with font that you are using. I have had things shift between Windows and Macs because the font renders a bit differently. Sometimes they take up more room on a Mac. So, you might want to make the font-size smaller or use a different font-family and see if that helps.

      If not, I wonder if you could put a screen shot somewhere that I could look at?

      ~ Lisa

  3. Thanks Lisa, nice article.

    There is a little problem, the menu seems transparent but I don’t see any opacity being used. What am I missing?

    1. Aris,

      I’m not sure what you mean that it seems transparent. There is a background color on the drop-down menus so it shouldn’t be transparent. Can you explain what part you are saying is transparent and let me know if you are talking about my demo or your implementation? I’ll try to help if I can.

      I’m planning on updating this page soon, because the code is a little bit dated now, but this should still work.

      ~ Lisa

  4. Hi Lisa,

    thanks for this post. I could not understand this piece in the css code:
    #home .home a, #home .home a:hover,
    #tutorials .tutorials a, #tutorials .tutorials a:hover,
    #about .about a, #about .about a:hover,
    #contact .contact a, #contact .contact a:hover,
    #news .news a, #news .news a:hover {
    background-color: #FFF;
    color: #000;
    cursor: default;

    I did not notice that you created elements with id home, tutorials, about, contact, and news. So I am wondering why is there an id selector (the #) in front of the class selector (the .).

    If you can help me to clarify my doubts, thank you so much in advance.

    Vonny Jap

    1. Hi Vonny,

      The purpose of that code is to style the active menu item differently.

      You are correct – I don’t really have elements with those IDs anywhere, except for the “About” page. What I was doing was saying if my website had a page for “Home”, “Contact”, “News”, and “Tutorials”, in addition to the “About” page, I would put an ID with those labels on the body tag corresponding page.

      If I had those 5 pages each with their own ID on the body tag, when I am actually looking at let’s say the Tutorials page, the browser would find #tutorials on the body tag, and then .tutorials on one of the menu items. I would be on the tutorials page, so that would be the “active” page, and it would be styled differently.

      That part of the code is not completely relevant to the drop-down menu part of the code. I probably muddied the waters a bit by adding it here. Another way to handle the active page is to add an “active” class to the navigation and style that instead. That probably would have been more clear. Also I am trying to not use ID’s in my CSS anymore, so I would use a slightly different solution today anyway.

      I don’t think I did a great job of explaining that, but hopefully you understand. If not, let me know and I will try again!

      ~ lisa

  5. Your post was the one that helped me to solve my problem after looking at many others. The different learning modalities and straight forward examples were great.

    Many thanks.

  6. Hi Lisa, your videos are are the most informative I’ve seen anywhere and I love the way you structure your website. I’ve found this video about drop-down navigation particularly useful.

    It would be great if you could make a video explaining how to make the drop-down menus ‘disappear’ and be replaced by those three horizontal lines in the top right corner (not sure what they’re called) when the screen gets small. I’ve only seen videos on how to do this using Bootstrap 3. It would be really helpful to see how to do it using just CSS.

    Many thanks, and keep up the good work.

      1. Hello Lisa,

        Your tutorials are the best I came across on the web! Keep up the good work!

        As Tony already requested, making the menu transform to a clickable ‘hamburger’ icon would be great. I would also appreciate if you could do a tutorial on that.

        Many thanks,


  7. Hi Lisa,

    I love this and your other tutorial about a simple CSS navigation bar, I learned a lot about CSS in general from watching them. I have two questions I hope you don’t mind helping with.

    On touch screens the hover effect that reveals the drop down lists doesn’t work in the same way as it does on a desktop or laptop with a mouse. If I look at my web site on my phone, I can touch the top menu item with my finger and the sub-menu drops down, but then it flashes away again before I can click on one of the items. This seems to be a problem of touch screen vs. mouse, not big screen vs. mobile.
    I once had my navigation on every page with a selector of some sort that highlighted the active page (I don’t remember exactly how) However, this got too difficult to maintain so I put my navigation bar into a separate file and now use a “php insert” command to include it in every page as it gets served. When I did this I couldn’t figure out how to highlight the active page. You talked about something along these lines when you were answering Vonny’s query but I didn’t understand how it might work in my case.


    Kind regards – David

    1. Hi David,

      Thanks. It is my intention to cover better mobile support for this navigation in a future tutorial, but I haven’t had time to tackle that yet, and it won’t be happening in the near future. Honestly, drop-down menus are becoming out of favor these days because of this very issue. Hover effects are not great to use on mobile devices. I’m sorry I don’t have any easy answer for you on this right now. There are others who have tackled this topic already, so hopefully you can find what you are looking for.

      For styling the active link, if you are doing a simple website and don’t really have a way to generate the active class from your server, something simple that you can do is add a class name to your body tag on each page. Make the class name match the class name on the navigation. (See this page for an example. https://github.com/lisacatalano/dropdown/blob/master/index.html) I have added the class news to the body. There is also a navigation element with a class of news. Each page will have the correct class name, and there will be a corresponding menu item.

      Then you can do a selector like:

      .news .news, 
      .about .about, 
      .contact .contact 
        background-color: #aaa;
        color: #444;
        cursor: default;

      With this code, when user is on the “News” page, only the navigation element that has a class of news will get the “active” styling. And the same for the other pages. You can be more specific with your selector, and say body.new li.news if you wanted, but it is not usually necessary.

      thanks, Lisa

  8. Hi Lisa,

    Thanks for that. I have 70+ pages on my web site and drop down menus are ideal for that, but obviously I can’t do a simple vertical list of these for smaller screens. I have also just realised that my phone (an HTC One) has a screen resolution of 1080 x 1920, which is the same as my desktop screen, so using the test that you show in the tutorial @media screen and (min-width: 650px) it would qualify as a “larger screen” although it is only a phone, and uses touch rather than a mouse.

    The first thought that entered my head was to use “on click” rather than “on hover” to show the drop down menus but although that might work for touch (I would have to change the top level menu item) it wouldn’t feel right for mouse. Is it possible to have a CSS “on either” condition? i.e. the menu drops down on either hover or click (touch).

    Do you have any recommendations for who has tackled this problem well for a site with many pages?

    Kind regards – David

    1. Hi David,

      There are ways to design a site with many pages that don’t involve drop-downs. One simple way to handle it would be to just have main pages for each of your major navigation areas (in my example “Tutorials” or “Newsletters”). Then on each page, you can have a different type of menu that shows what you show on your drop-down. The secondary menu could just be a second horizontal navigation menu that is at the top of the screen. It could also be a side menu, that ends up taking the full width on smaller screens.

      An interesting site to look at it http://www.usatoday.com (or any news site). They have a main horizontal navigation menu and if you click on most of those, you will see secondary menus when you get to the new page. There are no drop downs. Actually that is not really true, they do have a few areas that drop-down, like the weather, but it is not a crazy long 3 layer drop down menu to get you to where you want to go.

      Each main page is styled differently and there are some drop-downs on the secondary pages, but those drop downs don’t show up when you look on a mobile device. They use some sidebar navigation and some top navigation. Obviously, you wouldn’t need to be a complex as their site, but it might give you some idea on one way that you can pick and implement on all your main navigation pages.

      There are things you can do to make the drop-downs work on mobile devices that involve a little bit of JavaScript. If you still really want to use drop-down menus you can find a solution (I just don’t have a quick one to share with you right now.) I would encourage you to try to alter the design slightly to use a different navigation method that doesn’t rely on drop-downs. (Sounds silly saying this on my tutorial page for making a drop-down navigation. But I do believe long-term, that will be a better solution for you.)

      thanks, Lisa

  9. Hi,

    I tried this and it works brilliantly, except that when I try to print, the whole subsections are listed, i.e. tutorial#1, tutorial#2, … along with tutorial. They show up as a drop down menu, showing the full list

    Thank you !

  10. Wonderful stuff !

    All printing issues resolved too, with @media print { #nav {display: none}}
    I also took my redundant footer out of my prints.

    I owe you one

    Thank you !

  11. Hi Lisa

    Thanks for the tutorial, I’ve searched other sites and tried other code, yours works best for me, though I am having issue with trying to put a sub-menu inside a sub-menu, and have it sit along side the sub-menu list. Any Suggestions?

    1. Hi Adam,

      If you could put your code in something like CodePen where I can see it working, that would help me try to debug your problem.

      You can even click on “Edit in CodePen” in my demo above, and then make the changes there to make the code more like yours. You will just need to have a user account and save it, so that you can share the link.

      ~ Lisa

  12. Hi Lisa,

    Your tutorial seems quite straight but I am having problems creating the sub-menus as the css and navigation menu is already created and I need to tweak it myself. Do you think you can help me?


    1. Guari (and anyone else struggling to make this work),

      Sorry for the slow reply. If you can save your code in a CodePen (http://codepen.io/) or something similar, I can try to help you. You will need to set up a free account if you don’t have one, but it is the best way to show me your code.

      ~ Lisa

  13. for the submenu’s when i have a lot of text that would usually take two lines it’s making it only on one line and therefore unreadable is there a fix for this?

  14. hi,
    when i tried to use the menu with items that were more then a few words the items overlapped and became unreadable. when i changed the line-height to 24px everything seemed to be ok but the hover color change broke.

    1. Hi Felicyia,

      If you can save your code in a CodePen (http://codepen.io/) or something similar, I can try to help you. You will need to set up a free account if you don’t have one, but it is the best way to show me your code.

      ~ Lisa

      1. i eventually got the menu issue fixed after posting it on stock exchange but the newest issue i’m facing is having it be responsive instead of just the area around the words getting smaller (width) the whole thing just shifts down. on mobile this is fine but on just smaller monitors it’s not the #header i found was the issue. when i try to make the width a % the whole spacing just does wierd stuff.
        here’s where i’m testing it http://www.brokenarrowwear.com/redesign/layout.html

        1. Hi Felicyia,

          Sorry for the slow reply. I have been moving and not able to respond.

          Honestly for your website, I would consider making some of the text have less words in the menu. You can also use a slightly smaller font.

          As I’ve mentioned in previous comments, this solution is not an ideal responsive solution. I truly intend to work on tutorial on presenting a better solution, but I have not had the time since I’ve been moving.

          I’m sorry I don’t have a great solution for your issue at this time!

          thanks, Lisa

  15. Hey Lisa!
    First of all Thanks for such an informative video.
    Actually, I want my nested menu to occur horizontally on the TOP of the parent, rather than occurring below it.
    Plz help me in it.
    If you want I can provide you with my code.

  16. Hello Lisa,
    Thanks for this awesome tutorial, I am a student learning to code and I hope you can clear up my question.

    When you placed the sub menu to position absolute, to my surprise it did not position itself directly at the top left corner of the browser, but stayed below the main menu . I had understood that it would position itself to the DOM since no other position was stated therefore the browser would be its parent. It did not work out this way in this tutorial. I hope you can explain how this works.

    Also can you please explain the difference between styling
    .nav > ul > li {text-align: center; }
    .nav ul li { text-align: center; }
    What is the difference?

    I appreciate your help,
    thank you very much!

    1. Hi Joanna,

      The > is the child selector, or “direct descendant”. When you use it, you are saying you only want to target selectors that are directly underneath the parent.

      The example in the code is a little complicated because there are multiple >. But it is the same if you just think about one level.

      To keep it simpler:

      .nav > ul will only target the ul tags that are right inside of the .nav. When we have nested unordered lists (as we do in this tutorial) we only want the text-align: center to be applied to the first ul. The nested ul will not use that CSS.

      If you use .nav ul ALL ul tags inside of .nav will be affected.

      I hope that helps.

      For your first question, I would have to see the code to really answer. If you create a Pen on CodePen I could look at it. (Please don’t paste the code here in the comments. I need to see the code in action, and using http://codepen.io is a great way to do that. There are other sites that are similar if you have one you use.)

      ~ Lisa

  17. Hi Lisa

    This is an excellent tutorial. Practical and clear.

    My only criticism is that you need a like button! I scrolled up and down TWICE before I realised you didn’t have one. Gotta let us reward your good content. πŸ™‚



  18. why did u stop making video on youtube i start following ur cources in a about a week and i did finish watching all of them but i need more ……..anyway thanx for the good work and excuse my english

    1. Hi,

      I really miss making the videos, but I just have not had time this past year. Unfortunately, I am still too busy right now, but I am hoping I can start doing tutorials again this summer.

      ~ lisa

Leave a Reply

Your email address will not be published. Required fields are marked *