Overview
It is common practice to have the primary website navigation always visible on larger devices (desktop and tablet) and shown/hidden with a toggle button (“hamburger menu”) on smaller devices (mobile). Is it best practice to use the HTML5 dialog
element for this type of responsive navigation?
Dialog Pros
-
dialog
elements include a native keyboard trap. When a dialog is open, the tab index is isolated to the contents of the dialog, greatly improving the keyboard navigation experience. -
dialog
elements include native functionality to close model with the escape button. -
dialog
elements include native functionality to restore focus to the previously focused element when the modal is closed. -
dialog
elements include native functionality for a backdrop behind the modal. -
dialog
elements convey the intention of element to the browser allowing the browser to potentially render an element in the best possible way for the device. For example,select
elements are rendered different on desktop browser than mobile browsers.
Dialog Cons
-
There could be SEO implications to placing the primary navigation in a
dialog
element. In an effort to circumvent possible consequences, thedialog
element includes theopen
attribute. -
The
dialog
element is technically never opened on larger devices. Instead, thedialog
element is set todisplay: block
. If thedialog
is opened (dialog.show()
) when the browser width increases, it causes the focus to change. The `display:block; hack, circumvents the issue. This a typical implementation could cause issues on some devices.
Example
const dialog = document.querySelector('header dialog');
const header = document.querySelector('header');
function resize() {
if (!window.matchMedia('(max-width: 640px)').matches) {
dialog.close();
}
}
dialog.removeAttribute('open');
window.addEventListener('resize', resize);
resize();
document.querySelector('.open').addEventListener('click', () => {
dialog.showModal();
});
document.querySelector('.close').addEventListener('click', () => {
dialog.close();
});
body {
margin: 0;
}
main {
padding: 1rem;
}
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.75);
}
header dialog {
width: auto;
height: auto;
border: none;
padding: 1rem;
background-color: #333;
color: #FFF;
}
header dialog a {
color: #FFF;
}
header ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 1rem;
}
@media (min-width: 641px) {
header dialog {
position: static;
display: block; /* Force closed dialog to be visible on larger devices */
margin: 0; /* Necessary to fix niche rending bug when going from narrow to wide with an open dialog */
}
header .close,
header .open {
display: none !important;
}
}
@media (max-width: 640px) {
header dialog {
max-width: none;
max-height: none;
margin: 0;
width: auto;
padding: 1rem;
text-align: center;
}
header ul {
flex-direction: column;
margin-bottom: 1rem;
}
}
<header>
<dialog open> <!-- dialog is set to open in case search engines ignore closed dialogs -->
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<button class="close">Close</button>
</dialog>
<button class="open">Menu</button>
</header>