You will build a CSS dropdown that appears when a user hovers over or focuses on a menu trigger, using positioning and pseudo-classes.
What it is
A CSS dropdown is a hidden menu that becomes visible through styling rules. The usual mental model is a trigger and a panel: the trigger is the visible button or link, and the panel is the list of menu items placed below it. The panel is normally hidden with display: none, then shown when the parent menu is hovered or focused.
Related terms include positioning context, position: relative, position: absolute, :hover, and :focus-within.
Why it matters
- It keeps navigation compact by hiding secondary links until needed.
- It works without JavaScript for simple hover menus.
- Using
:focus-withinhelps keyboard users open and use the menu. - It is useful for navigation bars, account menus, and filter panels.
Syntax or steps
- Wrap the trigger and dropdown in a parent element.
- Give the parent
position: relativeso the dropdown is positioned inside it. - Give the dropdown
position: absoluteand hide it withdisplay: none. - Show the dropdown when the parent is hovered or focused.
Example
<nav class="menu">
<button class="trigger">Products</button>
<ul class="dropdown">
<li><a href="#pricing">Pricing</a></li>
<li><a href="#docs">Docs</a></li>
</ul>
</nav>
<style>
.menu {
position: relative;
display: inline-block;
}
.trigger {
padding: 8px 12px;
border: 1px solid #ccc;
background: #f7f7f7;
cursor: pointer;
}
.dropdown {
display: none;
position: absolute;
top: 100%;
left: 0;
z-index: 10;
margin: 0;
padding: 8px;
min-width: 160px;
list-style: none;
background: #fff;
border: 1px solid #ccc;
}
.menu:hover .dropdown,
.menu:focus-within .dropdown {
display: block;
}
.dropdown a {
display: block;
padding: 6px 8px;
color: #222;
text-decoration: none;
}
.dropdown a:hover,
.dropdown a:focus {
background: #eee;
}
</style>
The .menu element is the positioning context. The .dropdown is absolutely positioned below the trigger using top: 100%. The selector .menu:hover .dropdown shows the menu on mouse hover. The selector .menu:focus-within .dropdown shows the menu when the trigger or any link inside the dropdown receives keyboard focus.
Common mistakes
- Forgetting
position: relativeon the parent. The dropdown may appear in the wrong place. Fix it by making the parent the positioning context. - Using only
:hover. Keyboard users cannot open the menu. Add:focus-withinand make sure the trigger is focusable. - Expecting smooth animation with
display: none.displaycannot be transitioned. Usevisibilityandopacityif animation is needed. - Assuming CSS can update accessibility state. CSS cannot change
aria-expanded. For full accessibility, use JavaScript or a native disclosure element.
When to use it
CSS dropdowns are best for simple menus where hover and keyboard focus are enough. If you need click-to-open behavior, touch support, outside-click closing, or accurate accessibility state, JavaScript is usually better.
| Approach | Best for | Limitation |
|---|---|---|
| CSS hover and focus | Simple navigation menus | Poor for touch and state management |
| JavaScript toggle | Click, touch, and accessible menus | More code and event handling |
<details> |
Accessible disclosure menus | Not hover-based |
Practice
Guided exercise: add a second dropdown to the navigation bar. Make sure each dropdown opens only when its own parent menu is hovered or focused. Expected result: hovering Products shows only the Products menu, and hovering Support shows only the Support menu.
Challenge: replace display: none and display: block with visibility and opacity so the dropdown can fade in. Hint: use visibility: hidden; opacity: 0; by default, then set visibility: visible; opacity: 1; on hover or focus.
Quick check
Why use :focus-within instead of only :focus on the trigger?
Because focus may move from the trigger to a link inside the dropdown. :focus-within keeps the menu open while any descendant has focus.
Summary
CSS dropdowns reveal hidden menus using positioning and pseudo-classes. The key pattern is a relative parent, an absolute hidden panel, and a hover or focus rule that shows it.