Animated Hamburgers

By default, our mobile menu icon doesn’t do any animated when you click it. We prefer to skip the animation and move straight to a close icon, as it’s more lightweight that way.

However, if you want to animate your mobile menu icons (hamburger icons), you can do some with some custom code.

The first thing you’ll want to do is load their CSS: https://github.com/jonsuh/hamburgers

That’s a great library for animated hamburger icons.

You’ll want to load their base CSS, and then the CSS of the effect you want. For example, we’ll load the Collapse effect:

/*!
 * Hamburgers
 * @description Tasty CSS-animated hamburgers
 * @author Jonathan Suh @jonsuh
 * @site https://jonsuh.com/hamburgers
 * @link https://github.com/jonsuh/hamburgers
 */
.hamburger {
  padding: 15px 15px;
  display: inline-block;
  cursor: pointer;
  transition-property: opacity, filter;
  transition-duration: 0.15s;
  transition-timing-function: linear;
  font: inherit;
  color: inherit;
  text-transform: none;
  background-color: transparent;
  border: 0;
  margin: 0;
  overflow: visible; }
  .hamburger:hover {
    opacity: 0.7; }
  .hamburger.is-active:hover {
    opacity: 0.7; }
  .hamburger.is-active .hamburger-inner,
  .hamburger.is-active .hamburger-inner::before,
  .hamburger.is-active .hamburger-inner::after {
    background-color: #000; }

.hamburger-box {
  width: 40px;
  height: 24px;
  display: inline-block;
  position: relative; }

.hamburger-inner {
  display: block;
  top: 50%;
  margin-top: -2px; }
  .hamburger-inner, .hamburger-inner::before, .hamburger-inner::after {
    width: 40px;
    height: 4px;
    background-color: #000;
    border-radius: 4px;
    position: absolute;
    transition-property: transform;
    transition-duration: 0.15s;
    transition-timing-function: ease; }
  .hamburger-inner::before, .hamburger-inner::after {
    content: "";
    display: block; }
  .hamburger-inner::before {
    top: -10px; }
  .hamburger-inner::after {
    bottom: -10px; }

/*
   * Collapse
   */
.hamburger--collapse .hamburger-inner {
  top: auto;
  bottom: 0;
  transition-duration: 0.13s;
  transition-delay: 0.13s;
  transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); }
  .hamburger--collapse .hamburger-inner::after {
    top: -20px;
    transition: top 0.2s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), opacity 0.1s linear; }
  .hamburger--collapse .hamburger-inner::before {
    transition: top 0.12s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19); }

.hamburger--collapse.is-active .hamburger-inner {
  transform: translate3d(0, -10px, 0) rotate(-45deg);
  transition-delay: 0.22s;
  transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
  .hamburger--collapse.is-active .hamburger-inner::after {
    top: 0;
    opacity: 0;
    transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333), opacity 0.1s 0.22s linear; }
  .hamburger--collapse.is-active .hamburger-inner::before {
    top: 0;
    transform: rotate(-90deg);
    transition: top 0.1s 0.16s cubic-bezier(0.33333, 0, 0.66667, 0.33333), transform 0.13s 0.25s cubic-bezier(0.215, 0.61, 0.355, 1); }

Adding CSS

You can see why we choose to keep it simple by default, but let’s move on.

Next, we need to add some GeneratePress-specific CSS for the hamburgers:

.menu-toggle .mobile-menu,
.menu-toggle:before {
    display: none;
}

body .hamburger{
    transform: scale(.5);
    padding: 0;
    line-height: 0;
    vertical-align: middle;
}

body .hamburger.is-active .hamburger-inner::after,
body .hamburger-inner,
body .hamburger-inner::before, 
body .hamburger-inner::after,
body .hamburger.is-active .hamburger-inner, 
body .hamburger.is-active .hamburger-inner::before,
body .hamburger.is-active .hamburger-inner::after{
    background-color: currentColor;
}

Now we need to add their markup into our standard mobile menu toggle element. First, let’s add the function:

function tu_add_animated_hamburger_markup() {
	?>
	    <div class="hamburger hamburger--collapse">
	        <div class="hamburger-box">
	            <div class="hamburger-inner"></div>
	        </div>
	    </div>
	<?php
}

Adding PHP

If you’re using the standard mobile menu, you need to add this line:

add_action( 'generate_inside_mobile_menu', 'tu_add_animated_hamburger_markup' );

If you’re using the Mobile Header feature, you need this line instead:

add_action( 'generate_inside_mobile_header_menu', 'tu_add_animated_hamburger_markup' );

Now we need to add the necessary javascript to make the hamburger animate. You can do that with this function:

add_action( 'wp_footer', function() {
?>
	<script>
	var hamburgers = document.querySelectorAll(".hamburger"),
		menuToggle = document.querySelector( '.menu-toggle' ),
		menuItems = document.querySelectorAll( 'nav ul a' ),
		htmlEl = document.documentElement;

	menuToggle.addEventListener("click", function() {
		for ( var h = 0; h < hamburgers.length; h++ ) {
			hamburgers[h].classList.toggle("is-active");
		}
	} );

	for ( var i = 0; i < menuItems.length; i++ ) {
		menuItems[i].addEventListener( 'click', function( e ) {
			var closest_nav = this.closest( 'nav' );
			if ( closest_nav.classList.contains( 'toggled' ) || htmlEl.classList.contains( 'slide-opened' ) ) {
				var url = this.getAttribute( 'href' );
				var hash = url.split('#')[1];

				// Open the sub-menu if the link has no destination
				if ( '#' === url ) {
					e.preventDefault();
					for ( var h = 0; h < hamburgers.length; h++ ) {
						hamburgers[h].classList.toggle("is-active");
					}
				}
			}
		}, false );
	}

        var checkHamburgers = function() {
            var openedMobileMenus = document.querySelectorAll( '.toggled, .has-active-search' );

            for ( var i = 0; i < openedMobileMenus.length; i++ ) {
                var menuToggle = openedMobileMenus[i].querySelector( '.menu-toggle' );

                if ( menuToggle && menuToggle.offsetParent === null ) {
                    if ( openedMobileMenus[i].classList.contains( 'toggled' ) ) {
                        var hamburgers = document.querySelectorAll(".hamburger");

                        for ( var h = 0; h < hamburgers.length; h++ ) {
                            hamburgers[h].classList.remove("is-active");
                        }
                    }
                }
            }
        }
        window.addEventListener( 'resize', checkHamburgers, false );
        window.addEventListener( 'orientationchange', checkHamburgers, false );
	</script>
<?php
} );

Adding PHP

And that should be it for the standard mobile menu (regular and in the mobile header).

Off-Canvas Panel

Things complicate more if you’re using the off-canvas panel.

We need to add another PHP function to add more javascript to the page:

add_action( 'wp_enqueue_scripts', function() {
	$script =
		'var slideoutToggles = document.querySelectorAll( ".slideout-toggle a" ),
			 hamburgers = document.querySelectorAll( ".hamburger" ),
			 closeElements = document.querySelectorAll( ".slideout-overlay, .slideout-exit" );

		for ( var i = 0; i < slideoutToggles.length; i++ ) {
			slideoutToggles[i].addEventListener( "click", function( e ) {
				for ( var h = 0; h < hamburgers.length; h++ ) {
					if ( ! hamburgers[h].classList.contains( "is-active" ) ) {
						hamburgers[h].classList.add("is-active");
					}
				}
			} );
		};

		for ( var e = 0; e < closeElements.length; e++ ) {
			closeElements[e].addEventListener( "click", function( e ) {
				for ( var h = 0; h < hamburgers.length; h++ ) {
					hamburgers[h].classList.remove("is-active");
				};
			} );
		};';

	wp_add_inline_script( 'generate-offside', $script );
} );

Then we need some more CSS:

.slideout-toggle a:before,
.slide-opened .slideout-exit:not(.has-svg-icon):before,
.slideout-navigation button.slideout-exit:not(.has-svg-icon):before {
	display: none !important;
}

Make sure the “Close Button” is set to be “Inside” the panel for the effect to take place.