← HOME

ZORA WEB DESIGN

Hamburger menu desktop/mobile

By Julius

Build one slick Divi hamburger menu for desktop and mobile using a shortcode, Bootstrap 5, JavaScript, and lightweight CSS.

Hamburger menu desktop/mobile

Download JSON zipped Theme header

DOWNLOAD HERE

Hamburger menu desktop mobile is the goal of this guide. You want one menu. It should look slick on desktop. It should feel smooth on mobile. This tutorial shows how to do that in Divi with a shortcode, Bootstrap 5, and lightweight CSS.

In my video, I walk through the full setup. The code is below that video on the article. You’ll paste it where noted. Then you’ll style it in Divi. No extra heavy plugins are needed.

What You’ll Build

You’ll add a toggle button that opens a right-side offcanvas panel. The panel holds your main WordPress menu. On desktop, submenus open on click. On mobile, submenus use simple +/– toggles. It’s fast, clean, and easy to edit.

This is the hamburger menu desktop mobile pattern done right. One layout. Two behaviors tailored to screen size.

How It Works (High Level)

  1. Shortcode for the menu.
    The PHP snippet registers [hamburger]. It outputs your “hamburger” menu from WordPress Menus. You can rename the menu if yours has a different title.
  2. Bootstrap offcanvas layout.
    The HTML adds the toggle button and the offcanvas panel. You drop this in your Divi Theme Builder header. The shortcode sits inside the panel and prints your menu items.
  3. Desktop and mobile logic.
    The JS uses Bootstrap 5 for the offcanvas. It also adds click-to-open submenus on desktop widths. On mobile, the script enhances the default Divi mobile menu with +/– toggles.
  4. Polished CSS.
    The CSS styles the panel, links, nested lists, and social icons. It also handles spacing, hover states, and scroll behavior. The result is professional and consistent.

Where to Paste Each Piece

  • PHP → Use the Code Snippets plugin. Activate the snippet site-wide.
  • Bootstrap link + script + JS → Add in Divi Integration > Head.
  • Offcanvas HTML → Add to your Divi Theme Builder header using a Code or Text module. Place [hamburger] inside the offcanvas body.
  • CSS → Add in Divi Theme Options > Custom CSS or your child theme.

Setup Tips

  • In Appearance > Menus, create a menu named hamburger. Or update the snippet to match your menu name.
  • Keep labels short. It avoids wrapping in narrow panels.
  • Test nested levels. The CSS already indents deeper submenus.
  • Add your social icons right inside the offcanvas. Links are included in the code.

Why This Pattern

The hamburger menu desktop mobile approach gives you one code path. It avoids maintaining separate headers. Bootstrap handles the offcanvas mechanics. Your shortcode keeps the menu dynamic. Divi’s Theme Builder makes placement simple.

Troubleshooting

  • Offcanvas won’t open? Make sure the Bootstrap CSS and bundle JS are loaded once.
  • Submenus not toggling on desktop? Confirm the custom JS is in place and widths are above 980px.
  • Mobile menu overlapping content? Keep the provided CSS for fixed positioning and scroll.
  • Icons not showing? Ensure Font Awesome is loaded, or switch to Divi icons.

Helpful Resources (Outbound)

PHP Code (Place in new code on Code Snippets plugin)

function main_menu_shortcode() {
  ob_start();
  wp_nav_menu([
    'menu' => 'hamburger',
    'container' => false,
    'menu_class' => 'nav flex-column',
    'fallback_cb' => false,
    'items_wrap' => '<ul class="%2$s">%3$s</ul>',
  ]);
  return ob_get_clean();
}
add_shortcode('hamburger', 'main_menu_shortcode');

Bootstrap HTML code (place on menu in Theme Builder)

<!-- Menu Toggle Button -->
<button class="menu-toggle-btn ms-auto" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasNavbar" aria-controls="offcanvasNavbar" aria-label="Toggle navigation">
  <svg class="menu-icon" viewBox="0 0 100 100" width="50" height="50">
    <rect y="20" width="100" height="7" rx="3"></rect>
    <rect y="45" width="100" height="7" rx="3"></rect>
    <rect y="70" width="100" height="7" rx="3"></rect>
  </svg>
</button>

<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasNavbar" aria-labelledby="offcanvasNavbarLabel">
  <div class="offcanvas-header">
    <h5 class="offcanvas-title" id="offcanvasNavbarLabel"></h5>
    <button class="custom-close-btn" type="button" data-bs-dismiss="offcanvas" aria-label="Close">
      <i class="fas fa-times"></i>
    </button>
  </div>

  <div class="offcanvas-body">
    <div class="offcanvas-inner">

      [hamburger]

    </div>
  </div>
</div>

JS Code (Place on Integration - Head)

<!-- Bootstrap 5 CSS -->
<link
  href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
  rel="stylesheet"
/>

<!-- Bootstrap 5 Bundle JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

<script>
document.addEventListener("DOMContentLoaded", function () {
  const nav = document.querySelector("#offcanvasNavbar .offcanvas-body");

  if (nav && window.innerWidth > 980) {
    const dropdowns = nav.querySelectorAll(".menu-item-has-children");

    dropdowns.forEach(item => {
      const trigger = item.querySelector("a");

      if (trigger) {
        trigger.addEventListener("click", function (e) {
          e.preventDefault();

          const submenu = item.querySelector(".sub-menu");

          dropdowns.forEach(other => {
            if (
              other !== item &&
              !item.contains(other) &&
              !other.contains(item)
            ) {
              other.classList.remove("open");
              const otherSub = other.querySelector(".sub-menu");
              if (otherSub) otherSub.style.display = "none";
            }
          });

          item.classList.toggle("open");
          if (submenu) {
            submenu.style.display = item.classList.contains("open")
              ? "block"
              : "none";
          }
        });
      }
    });
  }

  function initMobileMenuToggles() {
    const menu = document.querySelector("ul.et_mobile_menu");
    if (!menu) return;

    const dropdownItems = menu.querySelectorAll(
      "li.menu-item-has-children, li.page_item_has_children"
    );

    dropdownItems.forEach(item => {
      if (!item.querySelector(".mobile-toggle")) {
        const toggle = document.createElement("a");
        toggle.href = "#";
        toggle.className = "mobile-toggle";
        toggle.textContent = "+";
        item.appendChild(toggle);

        toggle.addEventListener("click", function (e) {
          e.preventDefault();
          item.classList.toggle("dt-open");

          const submenu = item.querySelector("ul.children, ul.sub-menu");
          if (submenu) submenu.classList.toggle("visible");

          toggle.textContent = item.classList.contains("dt-open") ? "-" : "+";
        });
      }
    });
  }

  const hamburger = document.querySelector(".mobile_menu_bar");
  if (hamburger) {
    hamburger.addEventListener("click", function () {
      document.body.classList.toggle("menu-opened");

      if (document.body.classList.contains("menu-opened")) {
        setTimeout(initMobileMenuToggles, 100);
      }
    });
  }
});
</script>

Menu social code

<a href="https://facebook.com/yourpage" target="_blank"><i class="fab fa-facebook-f"></i></a>
<a href="https://instagram.com/yourusername" target="_blank"><i class="fab fa-instagram"></i></a>
<a href="https://youtube.com/yourchannel" target="_blank"><i class="fab fa-twitter"></i></a>

CSS Code

/* ===================================================================
   TABLE OF CONTENTS
   -------------------------------------------------------------------
   1. General Offcanvas Menu Styling
      1.1. Offcanvas Container & Layout
      1.2. Offcanvas Header
      1.3. Offcanvas Body & Inner Content
      1.4. Offcanvas Width Settings
   2. Menu Toggle Button (Hamburger Icon)
   3. General Menu Link Styling
      3.1. Main Menu Links
      3.2. Submenu Links
      3.3. Hover States
      3.4. List Reset
   4. Dropdown Menus (Hover & Click-Based Behavior)
      4.1. Parent Item Styling
      4.2. Arrow Symbols
      4.3. Submenu Container Styling
      4.4. Submenu Indentation
      4.5. Submenu Visibility (JS Controlled)
   5. Social Icons
      5.1. Offcanvas Social Icons
      5.2. Mobile Menu Social Icons
   6. Mobile Menu Styling (Divi Specific)
      6.1. Mobile Menu Container
      6.2. Mobile Menu Links
      6.3. Mobile Menu Toggle Icon (+/-)
      6.4. Mobile Submenu Visibility
      6.5. Mobile Submenu Link Styling
   7. Divi Specific Fixes
=================================================================== */

:root {
  grayish-black: #333333;
}

#offcanvasNavbar.offcanvas-end {
  width: 650px;
  max-width: 90%;
}

#offcanvasNavbar .offcanvas-header {
  padding-top: 50px;
  padding-bottom: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

#offcanvasNavbar .offcanvas-title,
#offcanvasNavbarLabel {
  color: black;
  font-size: 40px;
  text-align: left;
}

#offcanvasNavbar .custom-close-btn {
  font-size: 40px;
  font-weight: thin;
  color: black;
  background: none;
  border: none;
}

#offcanvasNavbar .custom-close-btn:hover {
  color: var(--grayish-black);
}

#offcanvasNavbar .offcanvas-body {
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

#offcanvasNavbar .offcanvas-inner {
  height: 100vh;
  overflow-y: auto;
  -ms-overflow-style: none;
  scrollbar-width: none;
  padding-left: 25px;
  padding-right: 25px;
}

#offcanvasNavbar .offcanvas-inner::-webkit-scrollbar {
  width: 0px;
  background: transparent;
  display: none;
}

@media (max-width: 767px) {
  #offcanvasNavbar.offcanvas-end {
    width: 100vw;
    max-width: 100vw;
  }
}

.menu-toggle-btn {
  background: none;
  border: none;
  padding: 0;
}

.menu-toggle-btn svg rect {
  fill: black;
  transition: fill 0.3s;
}

.menu-toggle-btn:hover svg rect {
  fill: var(--grayish-black);
}

#offcanvasNavbar .offcanvas-body .menu-item a {
  font-size: 24px;
  font-weight: 700;
  color: black;
  padding: 10px 0;
  margin-bottom: 4px;
  display: block;
  text-decoration: none !important;
  text-align: left;
  transition: color 0.3s;
  width: 100%;
  background: transparent !important;
}

#offcanvasNavbar .offcanvas-body .menu-item > a {
  color: black;
}

#offcanvasNavbar .offcanvas-body .sub-menu a {
  padding: 0.5px;
  margin: 1.5px;
  line-height: 1.3;
  font-weight: 600;
  font-size: 22px;
  color: black;
}

#offcanvasNavbar .offcanvas-body .sub-menu .sub-menu a {
  color: black;
  font-size: 20px;
}

#offcanvasNavbar .offcanvas-body .sub-menu .sub-menu .sub-menu a {
  color: black;
  font-size: 20px;
}

#offcanvasNavbar .offcanvas-body .sub-menu .sub-menu .sub-menu .sub-menu a {
  color: black;
}

#offcanvasNavbar .offcanvas-body .menu-item > a:hover {
  color: var(--grayish-black);
}

#offcanvasNavbar .offcanvas-body .sub-menu a:hover {
  color: var(--grayish-black);
}

#offcanvasNavbar .offcanvas-body .sub-menu .sub-menu a:hover {
  color: var(--grayish-black);
}

#offcanvasNavbar .offcanvas-body .sub-menu .sub-menu .sub-menu a:hover {
  color: var(--grayish-black);
}

#offcanvasNavbar .offcanvas-body .sub-menu .sub-menu .sub-menu .sub-menu a:hover {
  color: var(--grayish-black);
}

#offcanvasNavbar .offcanvas-body ul,
#offcanvasNavbar .offcanvas-body li {
  list-style: none;
  margin: 0;
  padding: 0;
}

#offcanvasNavbar .offcanvas-body .menu-item-has-children {
  position: relative;
}

#offcanvasNavbar .offcanvas-body .menu-item-has-children > a {
  position: relative;
  padding-right: 30px;
}

#offcanvasNavbar .offcanvas-body .menu-item-has-children > a::after {
  content: "⌵";
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  font-size: 24px;
  color: black;
  transition: transform 0.3s ease;
}

#offcanvasNavbar .offcanvas-body .menu-item-has-children.open > a::after {
  transform: translateY(-50%) rotate(90deg);
}

#offcanvasNavbar .offcanvas-body .sub-menu .menu-item-has-children > a::after {
  content: "+";
  font-size: 30px;
  color: black;
  transform: translateY(-50%);
}

#offcanvasNavbar .offcanvas-body .sub-menu .menu-item-has-children.open > a::after {
  content: "–";
  font-size: 30px;
}

#offcanvasNavbar .offcanvas-body ul.sub-menu {
  display: none;
  opacity: 0;
  visibility: hidden;
  position: absolute !important;
  top: 100%;
  left: 0;
  z-index: 999;
  padding: 0 !important;
  margin: 0 !important;
  width: 100% !important;
  background: white;
  box-shadow: none !important;
  transition: opacity 0.3s ease;
}

#offcanvasNavbar .offcanvas-body ul.sub-menu ul.sub-menu {
  padding-left: 0 !important;
  margin-left: 0 !important;
  width: 100% !important;
  position: static !important;
  background: white;
  box-shadow: none !important;
}

#offcanvasNavbar .offcanvas-body .menu-item-has-children > .sub-menu {
  margin-left: 5px !important;
  padding-left: 5px !important;
  width: calc(100% - 5px) !important;
}

#offcanvasNavbar .offcanvas-body ul.sub-menu > .menu-item-has-children > .sub-menu {
  margin-left: 20px !important;
  padding-left: 10px !important;
  width: calc(100% - 20px) !important;
}

#offcanvasNavbar .offcanvas-body ul.sub-menu ul.sub-menu > .menu-item-has-children > .sub-menu {
  margin-left: 30px !important;
  padding-left: 10px !important;
  width: calc(100% - 30px) !important;
}

#offcanvasNavbar .offcanvas-body ul.sub-menu ul.sub-menu ul.sub-menu > .menu-item-has-children > .sub-menu {
  margin-left: 40px !important;
  padding-left: 10px !important;
  width: calc(100% - 40px) !important;
}

#offcanvasNavbar .offcanvas-body .menu-item-has-children > .sub-menu {
  display: none;
  opacity: 1 !important;
  visibility: visible !important;
  position: static !important;
  margin-top: 10px;
}

#offcanvasNavbar .offcanvas-body .menu-item-has-children.open > .sub-menu {
  display: block;
  position: static !important;
}

#offcanvasNavbar .offcanvas-social {
  text-align: left;
  margin-top: 15px !important;
  margin-bottom: 20px;
}

#offcanvasNavbar .offcanvas-social a {
  color: black;
  font-size: 24px;
  margin-right: 12px;
  transition: color 0.3s ease;
}

#offcanvasNavbar .offcanvas-social a:hover {
  color: var(--grayish-black);
}

@media only screen and (max-width: 980px) {
  .et_mobile_menu .menu-item.social-icons {
    display: flex;
    gap: 10px;
    justify-content: center;
    align-items: center;
    width: 100%;
  }

  .et_mobile_menu .menu-item.social-icons a {
    display: inline-flex;
    align-items: center;
    font-size: 20px;
    color: white !important;
  }
}

.et_mobile_menu .menu-item.socials {
  display: flex;
  justify-content: flex-start;
  align-items: left;
  gap: 10px;
  padding: 10px 15px 25px 25px;
  width: 100%;
  margin: 20px;
  margin-left: -63px;
  box-sizing: border-box;
}

.et_mobile_menu .menu-item.socials a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: white;
  border-radius: 50%;
  font-size: 30px;
  color: black !important;
  text-decoration: none !important;
  transition: background 0.3s ease;
  margin: 0;
  padding: 0;
}

.et_mobile_menu {
  width: 100% !important;
  max-width: 100vw !important;
  left: 0 !important;
  right: 0 !important;
  position: fixed !important;
  top: 90px !important;
  z-index: 9999;
  background-color: black;
  padding-top: 20px;
  box-sizing: border-box;
  display: none;
  overflow-y: auto;
  max-height: calc(100vh - 80px);
}

body.menu-opened {
  overflow-x: hidden;
}

.menu-opened .et_mobile_menu {
  display: block;
}

.et_mobile_menu ul {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  padding: 0;
}

.et_pb_menu_0.et_pb_menu .et_mobile_menu,
.et_pb_menu_0.et_pb_menu .et_mobile_menu ul {
  background-color: white !important;
  border-radius: 10px;
}

.et_mobile_menu a {
  text-decoration: none !important;
}

.et_mobile_menu a,
.et_mobile_menu .menu-item-has-children > a {
  background-color: white !important;
  color: black !important;
}

ul.et_mobile_menu > li.menu-item-has-children,
ul.et_mobile_menu > li.page_item_has_children {
  position: relative;
}

ul.et_mobile_menu li.menu-item-has-children > a::after,
ul.et_mobile_menu li.page_item_has_children > a::after {
  content: none !important;
}

.mobile_menu_bar {
  font-size: 40px;
  color: black;
  cursor: pointer;
  position: fixed;
  top: 20px;
  right: 2px;
  z-index: 10001;
}

.mobile_menu_bar::before {
  content: '\61';
  font-family: 'ETModules';
  transition: content 0.3s ease;
}

.menu-opened .mobile_menu_bar::before {
  content: '\4d';
  color: black !important;
}

ul.et_mobile_menu li.menu-item-has-children .mobile-toggle,
ul.et_mobile_menu li.page_item_has_children .mobile-toggle {
  width: 44px;
  height: 100%;
  padding: 0 !important;
  max-height: 44px;
  border: none;
  position: absolute;
  right: 0;
  top: 16px;
  z-index: 999;
  background-color: transparent;
  font-family: Arial, sans-serif;
  font-size: 30px;
  color: black !important;
  display: inline-block;
  text-align: center;
  opacity: 1;
}

ul.et_mobile_menu .menu-item-has-children .sub-menu {
  display: none !important;
  visibility: hidden !important;
}

ul.et_mobile_menu .menu-item-has-children .sub-menu.visible {
  display: block !important;
  visibility: visible !important;
}

ul.et_mobile_menu .sub-menu .sub-menu {
  display: none !important;
  visibility: hidden !important;
}

ul.et_mobile_menu .sub-menu.visible .sub-menu {
  display: block !important;
  visibility: visible !important;
}

ul.et_mobile_menu .sub-menu a {
  background-color: white !important;
  color: black !important;
  padding-left: 4px;
  font-weight: normal;
}

ul.et_mobile_menu .sub-menu .sub-menu a {
  padding-left: 10px;
}

ul.et_mobile_menu .sub-menu .sub-menu .sub-menu a {
  padding-left: 20px;
}

.et_pb_button,
.et_pb_promo_button,
.et_pb_more_button,
button.et_pb_button,
a.et_pb_button {
  text-decoration: none !important;
}

#offcanvasNavbar .offcanvas-body ul.sub-menu {
  border: none !important;
  border-radius: 0;
  box-shadow: none !important;
}

.et_pb_text sup {
  font-size: 75%;
}