🎉📚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.
This week has been complicated, as we have started with accessible icons, added responsiveness to our navbars, and added a hamburger button. But we're not through! Now it's time to add a CSS-only dropdown to our work. As always, there's a ton to cover!
We'll continue working with the CodePen example from Day 13, which contains accessible icons in a responsive navbar with a hamburger button. Now it will also have a dropdown!
✅ Step 1: Add a second layer of HTML to our navbar
Follow along with today's CodePen example
As we covered back on Day 1 of the challenge, a second layer of navigation is an unordered list nested inside of a <li>
element. Here's the newest addition to our navbar; see the example for the rest of the HTML.
<li class="dropdown"><a href="https://jen4web.substack.com/about">About <span class="fas fa-caret-down" aria-hidden="true"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Mission</a></li>
<li><a href="#">Vision</a></li>
<li><a href="#">Board of Directors</a></li>
<li><a href="#">Careers</a></li>
</ul>
</li>
Let's run through each addition:
We've added a
class="dropdown"
to the<li>
element which will contain a dropdown, in addition to our second level navigation which includes a<ul>
with a class of.dropdown-menu
.Just after the "About" text, we've added an icon to indicate this is a dropdown menu. It's a small triangle pointing down.
Following the icon, we've added our second level of links as an unordered list and given it a class of
dropdown-menu
.And as discussed several times in the challenge, the second layer of navigation is contained INSIDE the <li></li> for About.
Having added this HTML, you should see a total mess! Yay!
(Note: if your screen/window isn't as wide as mine, some of the main navigation items may wrap onto the next row and look even more messy than this.)
🖥 Step 2: Making this a dropdown for desktop and tablet dimensions
Follow along with today's CodePen example
Consider your layouts across devices. It's unlikely you'll want a dropdown on mobile. You may or may not want one on tablet.
For this example, let's say we want the dropdowns on tablet and on desktop, but not on mobile. Therefore, we'll add styles to our tablet media query. And there's only a few needed to make this work!
Currently we have the dropdown visible in our design, so let's hide it.
.dropdown-menu {
display: none;
position: absolute;
z-index: 1000;
}
This is somewhat familiar, as we had similar settings yesterday on our hamburger menu. We set our menu display
to none
, so it's hidden away. We also set our position
to absolute
, because we'll want to move this around on the screen independently of its current location. The z-index ensures our menu is on top and not tucked inside some other element.
Next, we need to show our menu on hover. We'll also show it on focus. Focus is the state that happens as someone tabs through links on the web page.
.dropdown:hover .dropdown-menu,
.dropdown:focus-within .dropdown-menu {
display: block;
padding: 0.5rem;
background-color: #fae2d2;
}
The easy parts here are the styles. display: block makes the menu visible. padding gives it a bit of space around the edge, and we've assigned a light orange background color, so we can clearly see the menu.
The selectors are a little more challenging. Remember to read them from right to left. For a class of .dropdown-menu
, in which the :hover
state of the class of .dropdown
is true, AND for a class of .dropdown-menu
, in which the :focus-within
state of the class of .dropdown
is true, execute these styles.
You're familiar with hover, but maybe not with :focus-within, which is a new pseudo-class. MDN defines it this way:
The
:focus-within
CSS pseudo-class matches an element if the element or any of its descendants are focused. In other words, it represents an element that is itself matched by the:focus
pseudo-class or has a descendant that is matched by:focus
. (This includes descendants in shadow trees.)
Here, the descendants would be our second layer of navigation. That means that by including :focus-within
in this style, the menu will be exposed to tab through it in addition to seeing the menu when we hover over it.
Unfortunately, our menu isn't quite working yet! What's going on?
First, there's something weird happening with the alignment of the menu items with the first element and the second. Second, the menu is being cut off somehow. Where is that coming from?
Because we have so much code already in place from our hamburger button and responsive designs, we've introduced a few issues unknowingly in the last few days. In this case, the offender is the overflow
property we placed on nav
. We placed it there just in case any navigation got overly long.
In our mobile styles within the nav
declaration, overflow
is set to hidden
, and then we change that to auto
within the nav
style in the tablet media query. Let's change that value to visible instead, so you'll see this inside of the tablet media query:
@media (min-width: 550px) {
nav {
overflow: visible;
}
}
(Include all of the other style declarations that are in nav
as well! I removed them to keep the email shorter.)
With that property changed, you should now see the dropdown on desktop and on tablet.
Closer! Now we just have that mystery of why the "mission" link looks centered. It's the first link in the list... hmm... kind of like a first child...
@media (min-width: 550px) {
li:first-child a {
text-align: center;
}
}
At tablet dimensions, our logo is centered on its own line. It was never reset at desktop dimensions. If we set text-align: left
at desktop dimensions, the dropdown will be fixed on desktop.
However, when we go to tablet dimensions, that fix doesn't apply to the dropdown here:
Why is that? Because li:first-child a
is a pattern in our main navigation and in the second level of navigation.
The better fix is to override the style for the first child in the second level of navigation:
.dropdown li:first-child a {
text-align: left;
}
We'll place that in the tablet media query, which will carry into the desktop media query. Now the dropdown menu items are correctly aligned at desktop and tablet dimensions. Yay! Are we done yet?
📱 Step 3: What about mobile?
Follow along with today's CodePen example
Our navbar looks great on tablet and desktop, but what about mobile?
We've got all of the nav items displaying at the same level, so the organization of first level - second level is lost on the mobile display.
Fortunately, we've talked about styling multi-level vertical navbars before, so we can tweak our mobile styling to get a little order with these links.
Part of the reason for this chaotic look is that all of our nav items are currently centered on mobile. That was great with one level of navigation, but left alignment will work better with two levels of navigation. Therefore, outside all of the media queries and in the mobile styles, in ul change align-items: center
to align-items: flex-start
. This will align our items to the left.
ul {
align-items: flex-start;
}
All of our links look like they're equally important now. Let's style that second level of navigation to have a smaller font and an indent:
ul ul {
margin-left: 2rem;
font-size: 80%;
}
🥳 Whew! NOW we’re done! Right?
🧹 Step 4: Cleanup! 🧼
Anytime you add new styles outside of the media queries, there's a good chance you broke something affected by later media queries. Let's see what we broke this time. 😁
It's not too bad, but we do have a few tiny fixes to make.
First, the menu is indented and has a smaller font size on tablet and desktop. Considering we added a style that does exactly that for mobile, this is not surprising! To the tablet media query, then, let's add some styles to fix that:
@media (min-width: 550px) {
ul ul {
margin-left: 0rem;
font-size: 100%;
}
}
This removes the margin and resets our font size back to the original value of 1.2rem. You could also say font-size: 1.2rem
if that's clearer.
The other problem was obvious on our desktop display only. Here the navigation was pushed to the top of the page, rather than inline with the middle of the logo. Once again, what did we change? We set the ul style to align-items: flex-start. Let's reset that in the tablet media query.
@media (min-width: 550px) {
ul {
align-items: center;
}
}
But wait - doesn't this affect both levels of navigation? It would, except for one line of HTML:
<ul class="dropdown-menu">
Using CSS, we have .dropdown-menu
set to display: none
or display: block
, depending on whether the menu is showing or not. This overrides the earlier declaration of display: flex
on the ul
style.
Now you have a working responsive navbar, with accessible icons, with a hamburger button, and with a dropdown. Woo hoo!!!
🖥 Today’s CodePen example
👩🏽💻 Challenge: Martial arts and navbars part 3
👩🏽💻 Can you make a responsive navbar with accessible icons, CSS-only hamburger, and CSS-only dropdowns? We also offer ways to make this challenge easier.
📚 More information and examples
None today - we will exceed the email limit if we include them!