Accessible Megamenu + Ultimenu in Drupal
How we create an accessible mega-menu navigation experience on Western Drupal sites.
Ultimenu
Ultimenu is a Drupal module that allows you to create new block regions based on menu items. This makes for a very flexible mega menu setup, as you can add any block type to these regions.
We currently use Ultimenu version 2.12
Accessible Mega Menu
We use Adobe Accessible Mega Menu. This project is not actively maintained, and does require jQuery, but the tried-and-tested accessible functionality it offers to menus has been reliable for us for many years now.
Menu Initialization
We initialize the Adobe Mega Menu script with the classes that Ultimenu assigns to different parts of its menu, so that the functionality matches up. We also use this script to add some aria roles to active pages, which also serves to allow those menu items to be styled with CSS.
/* Accessibile mega menu variable declarations */
$('.main-navigation').accessibleMegaMenu({
/* prefix for generated unique id attributes, which are required
to indicate aria-owns, aria-controls and aria-labelledby */
uuidPrefix: "accessible-megamenu",
/* css class used to define the megamenu styling */
menuClass: "ultimenu",
/* css class for a top-level navigation item in the megamenu */
topNavItemClass: "ultimenu__item",
/* css class for a megamenu panel */
panelClass: "ultimenu__flyout",
/* css class for a group of items within a megamenu panel */
panelGroupClass: "sub-nav-group",
/* css class for the hover state */
hoverClass: "hover",
/* css class for the focus state */
focusClass: "focus",
/* css class for the open state */
openClass: "open"
});
// add aria-current to ultimenu link representing active page
var $active_item =
$('.main-navigation ul .ultimenu__item--active-page:not(.has-ultimenu');
var $homepage_link =
$('.main-navigation ul .ultimenu__item .ultimenu__link[href="/"]');
$active_item.children('.ultimenu__link').attr('aria-current', 'page');
if (window.location.pathname === $homepage_link.attr('href')) {
$homepage_link.parent('li').addClass('ultimenu__item--active-page');
$homepage_link.attr('aria-current', 'page');
}
Accessible Mega Menu Library
In ashlar.libraries.yml we set up our libraries:
accessible-mega-menu:
version: 1.x
js:
dist/js/accessible-mega-menu.js: {}
dependencies:
- core/jquery
- core/once
menu-extras:
version: 1.x
js:
build/js/menu-extras.js: {}
We then call them in our customized ultimenu.html.twig template:
{{ attach_library('ashlar/accessible-mega-menu') }}{{ attach_library('ashlar/menu-extras') }}
Custom Ultimenu Template
In addition to loading the Javascript libraries, our ultimenu.html.twig template also contains markup for icons in menu items. The final file looks like this:
{#
/**
* @file
* Default theme implementation for Ultimenu block (the UL list or container).
* If you need to customize the actual Flyout, use region.html.twig instead.
*
* Available variables:
* - items: The renderable array containing the menu.
* - attributes.class: A string containing the CSS classes for the SECTION tag:
* - ultimenu ultimenu--ORIENTATION ultimenu--MENU-NAME SKIN-NAME.
* - ultimenu ultimenu--horizontal ultimenu--htb ultimenu--horizontal ultimenu--blue
*
* The following variables are provided for contextual information.
* - delta: (string) The ultimenu's block delta which is the cleaned menu name.
* - config: An array of the block's configuration settings. Includes
* - menu_name: main-menu
* - skin: modules/custom/ultimenu/skins/ultimenu--htb-tabs-blue.css
* - skin_name: ultimenu--htb-tabs-blue (based on safe CSS file name)
* - orientation:
* - ultimenu--htb: horizontal to bottom
* - ultimenu--htt: horizontal to top
* - ultimenu--vtr: vertical to right
* - ultimenu--vtl: vertical to left
*
* @see template_preprocess_ultimenu()
*/
#}
{{ attach_library('ashlar/accessible-mega-menu') }}
{{ attach_library('ashlar/menu-extras') }}
{%
set classes = [
'ultimenu',
'ultimenu--' ~ delta,
config.orientation ? config.orientation|clean_class,
'v' in config.orientation ? 'ultimenu--vertical' : 'ultimenu--horizontal',
config.skin_name ? config.skin_name|clean_class,
]
%}
{%
set item_classes = [
'ultimenu__item',
'uitem',
]
%}
{% set id_attributes = attributes.id|clean_id %}
{% apply spaceless %}
{% if items -%}
<nav class="main-navigation wwu-menu-closed" aria-label="Main">
{% include "@templates/label/label.twig" %}
<ul{{ attributes.addClass(classes) }}>
{%- for item in items -%}
<li{{ item.attributes.addClass(item_classes, item.flyout ? 'has-ultimenu', item.config.is_active == true ? 'ultimenu__item--active-page')}}>
{%- if item.link -%}
{% if item.title == 'home-icon' %}
{% block home %}
<a class="ultimenu__link" href="//webtech.wwu.edu/%7B%7B%20url%28%27%3Cfront%3E%27%29%20%7D%7D"><span class="material-icons">home</span></a>
{% endblock %}
{% else %}
{% if item.flyout %}
<span class="component-icon expand-icon mobile" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg></span>
<span class="component-icon expand-icon desktop" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/></svg></span>
{% endif %}
{{ item.link }}
{% endif %}
{% if item.below %}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% endif %}
{%- endif -%}
{%- if item.flyout -%}
<section{{ item.flyout_attributes.addClass('ultimenu__flyout') }}>
{{ item.flyout }}
</section>
{%- endif -%}
</li>
{%- endfor -%}
</ul>
</nav>
{%- endif %}
{% endapply %}
Ultimenu Setup
The final step is to install and set up Ultimenu, which we outline in our Menu Setup tutorial.