About The Author

Mike is a senior front-end developer at AVB Digital, as well as a maintainer and lead dev of the Drupal core’s Olivero theme. He’s a big believer in free open … More about Mike ↬

Email Newsletter

Weekly tips on front-end & UX .
Trusted by 200,000+ folks.

In this article, Mike Herchel covers Drupal’s new default theme, Olivero, which is usable, accessible, robust, and beautiful and will help you improve websites’ navigation systems.

A website’s primary navigation is critical to its usability and accessibility. However, navigation systems are deceptively complicated. All but the simplest websites have to deal with this.

With version 9.4, Drupal has a brand new default theme called Olivero. Being the default, we knew its navigation system would be used by hundreds of millions (if not billions) of users throughout its lifetime. And of all the things that we are proud of with Drupal’s new theme, the navigation system tops the list. An enormous amount of testing, bug fixes, and care went into it.

Usable, Accessible, Robust, And Beautiful

When we started creating the theme, we knew it needed to be usable, accessible, robust, and beautiful. All of these goals pose significant challenges.

For usability, we wanted to include second-level navigation drop-downs similar to many sites on the internet. These second-level navigation drop-downs need to open on hover, click, and touch.

Accessibility is a Drupal core gate. We knew it must meet or exceed WCAG 2.1 AA standards. More than just meeting the standards, we want our theme to be a delight to navigate for those who need to use assistive technology.

And, because we don’t control the content, the menu system needed to be very robust. We don’t know if the content editors will enter one item or hundreds! We don’t have control over the length of the text. We also support internationalization, which includes supporting right-to-left languages such as Arabic.

On top of this functionality, the menu also needed to be beautiful. Our designers did an amazing job mocking it up, and then we integrated some basic CSS transitions to add a slight fade-in, vertically transformed animation.

Creating The Markup For Our Menu

Olivero’s menu starts with a standard

More after jump! Continue reading below ↓

Desktop Menu Features

Opening The Desktop Submenus

The submenus open on hover, click, and tap. However, we need to ensure that these events don’t fire simultaneously (like they can on touch devices) because if the menu instantaneously opens and closes, it’ll seem like nothing has happened! We also need to consider assistive technology like point-scanning tools that may trigger both events in rapid succession.

MacOS pointer scanning tool in action (speeded up).

To accommodate all of this, we listen for a touchstart event and, if present, skip the mouseover event processing. If and when the mouseover event processes, we disable the click event from doing anything for half a second. And finally, when the click event processes, we show the submenu. The logic gets a little complicated, but it’s usable with any assistive technology.

Closing The Desktop Submenus

The submenus need to close when certain conditions are met:

  • If the Escape key is pressed, the submenu will close, and the focus will return to the parent item.
  • If a mouseout event occurs, the submenu will close unless focus is contained within the submenu.
  • Similarly, the submenus will close on the blur event to ensure that the submenus cannot obscure one another (and potentially violate the WCAG 2.4.7 focus visible success criterion).

Accommodating Large Amounts Of Menu Items

Because the theme can’t control how many menu items the user enters, we have to accommodate an unlimited amount. We built an option to turn on the mobile menu (which can accommodate unlimited items) at all screen widths. But there is still the edge case where there may be enough items at medium widths to trigger the menu to wrap or overflow.

Wrapped menu with too many items
Menu is wrapping because it has too many items on a narrow viewport. ( Large preview )

To accommodate this, we switch to the mobile menu when the primary menu runs out of space. To do this, we set a resize observer to trigger a check to see if the text is wrapped. If it is, we enable the mobile menu and remember when to transition back to the desktop version (if the viewport is enlarged).

		const navMenu = document.querySelector('.primary-nav'); const navItem = navMenu.querySelector('.primary-nav__menu-item'); function checkIfDesktopNavigationWraps() { if (isDesktopNav() && navMenu.height > navItem.clientHeight) { enableMobileNav(); // Enable the mobile navigation. // Remember when to switch back to desktop navigation. const navMediaQuery = window.matchMedia(`(max-width: ${window.innerWidth + 15}px)`); navMediaQuery.addEventListener('change', () => { // Double check to see if the navigation is wrapping to prevent edge // cases where the mobile menu should still be enabled. if (navMenu.clientHeight > navItem.clientHeight) { disableMobileNav(navMenu, navItem); } }, { once: true }); } } const resizeObserver = new ResizeObserver(checkIfDesktopNavigationWraps); resizeObserver.observe(navMenu);
A screenshot with a mobile menu enabled
Much better. The mobile menu is now enabled because the desktop navigation could not accommodate all of the menu items. ( Large preview )

Make Sure Submenus Cannot Overflow The Viewport

Olivero’s menu is fixed to the top of the viewport. Fixed menus can create a problem if the viewport is shorter than the longest submenu — the user will never be able to scroll to access the items at the end. This inability to access items at the end of the menu creates another failure of WCAG 2.4.7 Focus Visible .

A screenshot where the long menu items are inaccessible below viewport
With a fixed header, and short viewport, the long menu items are not reachable by scrolling or tabbing. ( Large preview )

We solve this by calculating the height of the header and setting max-height and overflow: auto on the submenu.

	.submenu { max-height: calc(100vh - var(--header-height)); overflow: auto; }

With these styles in place, the menu will never grow larger than the viewport height, and the browser will make the submenu scrollable only if needed. If the user tabs to the bottom of the submenu, the browser will automatically scroll the content into view.

A screenshot where the submenu has a scrollbar
With the styles applied, the submenu gets a scrollbar if the content is larger than the viewport height. ( Large preview )

Non-JavaScript Support

Because Drupal renders its markup on the server, we have the opportunity to support devices where JavaScript is disabled. To make this happen, we enable :hover and :focus-within on the parent menu item.

	body:not(.js) .menu-item:is(:hover, :focus-visible) .menu-level-2 { visibility: visible; }

Mobile Menu Features

Olivero’s mobile menu functions much as you’d expect. It does not react to hovers, but the aria attributes stay the same.

Olivero’s mobile navigation
( Large preview )

The mobile navigation is activated by a