<!-- for use with <use> -->
<svg xmlns="http://www.w3.org/2000/svg"  hidden>
	<symbol id="arrow" viewbox="0 0 16 16" >
		<polyline points="4 6, 8 10, 12 6" stroke="#000" stroke-width="2" fill="transparent" stroke-linecap="round" />
	</symbol>
</svg>

<!-- In the real world, all hrefs would have go to real, unique URLs, not a "#" -->
<nav id="site-navigation" class="site-navigation" aria-label="Clickable Menu Demonstration">
	<ul class="main-menu clicky-menu no-js">
		<li>
			<a href="#">Home</a>
		</li>
		<li>
			<a href="#">
				Services
				<svg aria-hidden="true" width="16" height="16">
					<use xlink:href="#arrow" />
				</svg>
			</a>
			<ul>
				<li><a href="#">Design</a></li>
				<li><a href="#">Development</a></li>
				<li><a href="#">Accessibility</a></li>
				<li><a href="#">Content Strategy</a></li>
				<li><a href="#">Training</a></li>
			</ul>
		</li>
		<li>
			<a href="#">
				Portfolio
				<svg aria-hidden="true" width="16" height="16">
					<use xlink:href="#arrow" />
				</svg>
			</a>
			<ul>
				<li><a href="#">Nonprofits</a></li>
				<li><a href="#">Higher Education</a></li>
				<li><a href="#">Associations</a></li>
				<li><a href="#">Consultants</a></li>
			</ul>
		</li>
		<li>
			<a href="#">
				About
				<svg aria-hidden="true" width="16" height="16">
					<use xlink:href="#arrow" />
				</svg>
			</a>
			<ul>
				<li><a href="#">Mission</a></li>
				<li><a href="#">History</a></li>
				<li><a href="#">Contact</a></li>
			</ul>
		</li>
	</ul>
</nav>

<p class="github"><a href="https://github.com/mrwweb/clicky-menus">Now on Github</a></p>
/**
 * Initial state, hidden off screen
 */
.clicky-menu ul {
	position: absolute;
	top: 100%;
	left: 0;
	visibility: hidden; /*[1]*/
}

/**
 * No JS fallback
 *
 * Triggers menus on hover rather than click. Supports keyboard navigation in modern browsers.
 */
.clicky-menu.no-js li:hover > ul {
	visibility: visible;
}
.clicky-menu.no-js li:focus-within > ul { /*[2]*/
	visibility: visible;
}

/**
 * Open/Close Menu Behavior with JS
 */
.clicky-menu ul[aria-hidden="false"] {
	visibility: visible;
}

/* Prevent offscreen-submenus */
.clicky-menu .sub-menu--right {
	left: auto !important;
	right: 0 !important;
}

/**
 * Footnotes
 *
 * [1] Using `visibility` instead of `display` allows for easier transitions and animation of submenus
 * [2] Must be a separate ruleset so that hover works in non-modern browsers
 */


/* DEMO CSS */
body {
	margin: 0;
	min-height: 100vh;
	background: linear-gradient(17deg, teal, purple, orange) center/cover no-repeat;
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
				 Roboto, Oxygen-Sans, Ubuntu, Cantarell,
				 "Helvetica Neue", sans-serif;
} 

/* Hidden SVG used for down arrows */
svg[hidden] {
	display: none;
	position: absolute;
}

.site-navigation {
	width: 86%;
	max-width: 782px;
	margin: 100px auto 300px;
	box-shadow: 2px 2px 4px rgba(0,0,0,.2);
	background-color: #eee;
	border-radius: 4px;
}

.clicky-menu {
	justify-content: stretch;
	margin: 0;
	padding: 0;
	list-style: none;
}
@media (min-width: 540px) {
	.clicky-menu {
		display: flex;
	}
}

/* General Link & Button Styles */
.clicky-menu a,
.clicky-menu button {
	margin: .25em;
	padding: 1em;
	background: transparent;
	color: #000;
	font-weight: bold;
	text-decoration: none;
	font-family: inherit;
	border-radius: 3px;
}

.clicky-menu a:hover,
.clicky-menu button:hover {
	background: #fff;
}

.clicky-menu a:focus,
.clicky-menu button:focus {
	outline: .125em dotted purple;
	outline-offset: -.125em;
}

/* Top Level Items */
.clicky-menu > li {
	position: relative;
	flex: 1 1 auto;
	display: flex;
	justify-content: stretch;
	flex-wrap: wrap;
}

.clicky-menu > li > a,
.clicky-menu > li > button {
	flex: 1 0 auto;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	border: 0;
	font-size: inherit;
	line-height: 1.5;
	cursor: pointer;
}
@media (min-width: 540px) {
	.clicky-menu > li > a,
	.clicky-menu > li > button {
		justify-content: center;
	}	
}

/* Icon */
.clicky-menu svg {
	width: 1em;
	height: 1em;
	margin-left: .5em;
}

.clicky-menu [aria-expanded="true"] svg {
	transform: scaleY(-1);
}

/* Submenu Styles */
.clicky-menu ul {
	min-width: 100%;
	width: 12em;
	margin-top: .25em;
	padding: 0;
	list-style: none;
	background-color: #eee;
	border-radius: 3px;
}
@media (min-width: 540px) {
	.clicky-menu {
		box-shadow: 2px 4px 4px rgba(0,0,0,.2);
	}
}

/* Responsive Submenu Behavior */
.clicky-menu ul[aria-hidden="false"] {
	position: static;
	width: 100%;
	flex: 0 0 auto;
}

@media (min-width: 540px) {
	.clicky-menu ul[aria-hidden="false"] {
		position: absolute;
		width: auto;
	}
}

/* Submenu Links */
.clicky-menu ul a {
	display: block;
	padding-top: .375em;
	padding-bottom: .375em;
}
@media (min-width: 540px) {
	.clicky-menu ul a {
		padding: .375em 1em;
		white-space: nowrap;
	}
}

.github {
	text-align: center;
	a {
		color: #fff;
		opacity: .6;
		
		&:hover,
		&:focus {
			opacity: 1;
		}
	}
}
View Compiled
/**
 * Object for creating click-triggered navigation submenus
 *
 * Latest version, Issues, etc: https://github.com/mrwweb/clicky-menus
 *
 * Thanks for the inspiration:
 * 		- https://www.lottejackson.com/learning/a-reusable-javascript-toggle-pattern
 * 		- https://codepen.io/lottejackson/pen/yObQRM
 */

(function() {

	'use strict';

	const ClickyMenus = function( menu ) {

		// DOM element(s)
		let	container = menu.parentElement,
			currentMenuItem,
			i,
			len;

		this.init = function() {
			menuSetup();
			document.addEventListener( 'click', closeOpenMenu );
		}


		/*===================================================
		=            Menu Open / Close Functions            =
		===================================================*/
		function toggleOnMenuClick( e ) {

			const button = e.currentTarget;

			// close open menu if there is one
			if ( currentMenuItem && button !== currentMenuItem ) {
				toggleSubmenu( currentMenuItem );
			}

			toggleSubmenu( button );

		};

		function toggleSubmenu( button ) {

			const submenu = document.getElementById( button.getAttribute( 'aria-controls' ) );

			if ( 'true' === button.getAttribute( 'aria-expanded' ) ) {

				button.setAttribute( 'aria-expanded', false );
				submenu.setAttribute( 'aria-hidden', true );
				currentMenuItem = false;

			} else {

				button.setAttribute( 'aria-expanded', true );
				submenu.setAttribute( 'aria-hidden', false );
				preventOffScreenSubmenu( submenu );
				currentMenuItem = button;

			}

		};

		function preventOffScreenSubmenu( submenu ) {

			const 	screenWidth =	window.innerWidth ||
									document.documentElement.clientWidth ||
									document.body.clientWidth,
					parent = submenu.offsetParent,
					menuLeftEdge = parent.getBoundingClientRect().left,
					menuRightEdge = menuLeftEdge + submenu.offsetWidth;

			if ( menuRightEdge + 32 > screenWidth ) { // adding 32 so it's not too close
				submenu.classList.add( 'sub-menu--right' );
			}

		}

		function closeOnEscKey(e) {

			if(	27 === e.keyCode ) {

				// we're in a submenu item
				if( null !== e.target.closest('ul[aria-hidden="false"]') ) {
					currentMenuItem.focus();
					toggleSubmenu( currentMenuItem );

				// we're on a parent item
				} else if ( 'true' === e.target.getAttribute('aria-expanded') ) {
					toggleSubmenu( currentMenuItem );
				}

			}

		}

		function closeOpenMenu( e ) {

			if ( currentMenuItem && ! e.target.closest( '#' + container.id ) ) {
				toggleSubmenu( currentMenuItem );
			}

		};

		/*===========================================================
		=            Modify Menu Markup & Bind Listeners            =
		=============================================================*/
		function menuSetup() {

			menu.classList.remove('no-js');

			menu.querySelectorAll('ul').forEach( ( submenu ) => {

				const menuItem = submenu.parentElement;

				if ( 'undefined' !== typeof submenu ) {

					let button = convertLinkToButton( menuItem );

					setUpAria( submenu, button );

					// bind event listener to button
					button.addEventListener( 'click', toggleOnMenuClick );
					menu.addEventListener( 'keyup', closeOnEscKey );

				}

			});

		};

		/**
		 * Why do this? See https://justmarkup.com/articles/2019-01-21-the-link-to-button-enhancement/
		 */
		function convertLinkToButton( menuItem ) {

			const 	link = menuItem.getElementsByTagName( 'a' )[0],
					linkHTML = link.innerHTML,
					linkAtts = link.attributes,
					button = document.createElement( 'button' );

			if( null !== link ) {

				// set button content and attributes
				button.innerHTML = linkHTML.trim();
				for( i = 0, len = linkAtts.length; i < len; i++ ) {
					let attr = linkAtts[i];
					if( 'href' !== attr.name ) {
						button.setAttribute( attr.name, attr.value );
					}
				}

				menuItem.replaceChild( button, link );

			}

			return button;

		}

		function setUpAria( submenu, button ) {

			const submenuId = submenu.getAttribute( 'id' );

			let id;
			if( null === submenuId ) {
				id = button.textContent.trim().replace(/\s+/g, '-').toLowerCase() + '-submenu';
			} else {
				id = menuItemId + '-submenu';
			}

			// set button ARIA
			button.setAttribute( 'aria-controls', id );
			button.setAttribute( 'aria-expanded', false );

			// set submenu ARIA
			submenu.setAttribute( 'id', id );
			submenu.setAttribute( 'aria-hidden', true );

		}

	}

	/* Create a ClickMenus object and initiate menu for any menu with .clicky-menu class */
	document.addEventListener('DOMContentLoaded', function(){
		const menus = document.querySelectorAll( '.clicky-menu' );

		menus.forEach( menu => {

			let clickyMenu = new ClickyMenus(menu);
			clickyMenu.init();

		});
	});

}());

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.