Build a WordPress dropdown menu with semantic HTML, CSS media queries, and a small JavaScript toggle. That gives you control, speed, and fewer plugin conflicts. A solid menu should work with a mouse, keyboard, touch screen, and screen reader.
TLDR: Use WordPress’ built-in wp_nav_menu(), style dropdowns with CSS, and add JavaScript only for mobile toggles and accessibility states. For example, a service business with 18 menu links can group pages under “Services” and “Locations,” cutting visible header links from 18 to 5. In many small-site audits, cleaner menus reduce mobile header clutter by 60% or more. The result is faster access, fewer taps, and a site that feels less messy.
A dropdown menu is not just decoration. It affects how people reach products, service pages, pricing, contact forms, and support content. If it breaks on mobile, users leave. If it hides too much, users miss key pages. The goal is simple: show primary choices first, then reveal secondary pages only when needed.
WordPress already gives you the menu management tools. You do not need a heavy plugin for a standard dropdown. Honestly, it feels like overkill when a plugin adds six CSS files just to open a submenu. A careful theme setup is usually faster and easier to maintain.
Start with the WordPress menu structure
Go to Appearance > Menus in the WordPress admin area. Create a menu, add your pages, then drag child items under parent items. WordPress will output nested lists, which are perfect for dropdowns.
In your theme header file, place the menu where the site header sits:
<button class="menu-toggle" aria-expanded="false" aria-controls="primary-menu">
Menu
</button>
<nav class="site-menu" aria-label="Primary menu">
<?php
wp_nav_menu(array(
'theme_location' => 'primary',
'menu_id' => 'primary-menu',
'container' => false
));
?>
</nav>
Register the menu location in functions.php if your theme does not already have one:
function theme_register_menus() {
register_nav_menus(array(
'primary' => __('Primary Menu', 'yourtheme')
));
}
add_action('after_setup_theme', 'theme_register_menus');
This creates a reliable base. WordPress handles the menu tree. CSS handles layout. JavaScript handles small-screen opening behavior.
Style the desktop dropdown with CSS
Use plain CSS first. The desktop version should open on hover and keyboard focus. Keep spacing generous. Tiny menu targets are painful, especially on laptops with trackpads.
.site-menu ul {
list-style: none;
margin: 0;
padding: 0;
}
.site-menu > ul {
display: flex;
gap: 1.5rem;
align-items: center;
}
.site-menu li {
position: relative;
}
.site-menu a {
display: block;
padding: 0.75rem 0.5rem;
color: #111;
text-decoration: none;
font-weight: 600;
}
.site-menu .sub-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
min-width: 220px;
background: #fff;
border: 1px solid #ddd;
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
z-index: 50;
}
.site-menu li:hover > .sub-menu,
.site-menu li:focus-within > .sub-menu {
display: block;
}
.site-menu .sub-menu a {
padding: 0.75rem 1rem;
font-weight: 500;
white-space: nowrap;
}
.site-menu a:focus {
outline: 3px solid #2563eb;
outline-offset: 2px;
}
The :focus-within rule matters. It lets keyboard users open submenu items with the Tab key. Without it, dropdowns often work only for mouse users, which is a serious usability failure.
Make it responsive for mobile screens
On smaller screens, do not rely on hover. Phones do not have hover in the same reliable way. Use a button to open the menu, then stack the links vertically.
.menu-toggle {
display: none;
padding: 0.75rem 1rem;
border: 1px solid #111;
background: #fff;
font-weight: 700;
}
@media (max-width: 768px) {
.menu-toggle {
display: inline-block;
}
.site-menu {
display: none;
}
.site-menu.is-open {
display: block;
}
.site-menu > ul {
display: block;
}
.site-menu li {
border-bottom: 1px solid #eee;
}
.site-menu .sub-menu {
position: static;
display: none;
border: 0;
box-shadow: none;
padding-left: 1rem;
}
.site-menu li.is-open > .sub-menu {
display: block;
}
}
This layout is plain, but dependable. A mobile visitor can see the main sections first. When they need more, they can open a parent item. No sideways scrolling. No hidden text. No guessing.
Add JavaScript for toggles and ARIA states
JavaScript should do one job: open and close things. Keep it small. Expect to waste time on strange bugs if your script tries to rebuild the full menu in the browser.
document.addEventListener('DOMContentLoaded', function () {
const toggle = document.querySelector('.menu-toggle');
const menu = document.querySelector('.site-menu');
if (!toggle || !menu) return;
toggle.addEventListener('click', function () {
const isOpen = menu.classList.toggle('is-open');
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
const parents = menu.querySelectorAll('.menu-item-has-children > a');
parents.forEach(function (link) {
link.addEventListener('click', function (event) {
if (window.innerWidth > 768) return;
event.preventDefault();
const item = link.parentElement;
const isOpen = item.classList.toggle('is-open');
link.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
link.setAttribute('aria-expanded', 'false');
});
});
For parent links on mobile, this script opens the submenu instead of sending the visitor away at once. If the parent page is important, add it again as the first child item, such as “All Services.” That avoids trapping content behind a tap action.
Enqueue the script correctly
Do not paste scripts randomly into the header. Add files through WordPress enqueue functions. This keeps caching cleaner and reduces conflicts.
function theme_menu_assets() {
wp_enqueue_style(
'theme-menu',
get_template_directory_uri() . '/assets/css/menu.css',
array(),
'1.0'
);
wp_enqueue_script(
'theme-menu',
get_template_directory_uri() . '/assets/js/menu.js',
array(),
'1.0',
true
);
}
add_action('wp_enqueue_scripts', 'theme_menu_assets');
The final true loads the script near the closing body tag. That usually improves page loading because the browser can render content before running the menu script.
Accessibility and usability checks
A responsive dropdown should pass a few practical tests. Use this checklist before shipping:
- Keyboard access: Tab through every top-level and child link.
- Visible focus: Make sure focused links have a clear outline.
- Touch targets: Keep links at least 44 pixels tall where possible.
- Readable labels: Use “Pricing,” not vague labels like “Info.”
- Small-screen testing: Check widths around 320, 375, 768, and 1024 pixels.
- No link overload: Keep top-level items near 5 to 7 choices.
Analytics can guide menu choices. If a “Blog” link gets 1.2% of header clicks but “Book a Call” gets 21%, give the stronger action better placement. Do not let internal politics decide the header. Let user behavior help.
Common mistakes to avoid
Many dropdown menus fail for small reasons. The fixes are usually simple.
- Using hover only: This breaks down on touch devices.
- Hiding focus styles: Keyboard users lose their place.
- Creating deep menus: Three or four levels feel slow and confusing.
- Loading too many assets: A menu should not add major page weight.
- Ignoring parent links: Decide whether they open a submenu or lead to a page.
A clean WP dropdown menu is built from structure first, then presentation, then behavior. Use WordPress to manage pages, CSS to control layout, and JavaScript to support mobile interaction. Keep the code modest. Test with real devices. The best menu is the one users barely think about because it simply works.