
The Final Off-Canvas Navigation?
The Final Off-Canvas Navigation? ź“ė Ø
Warning
This post was written 5 years ago!
Personal opinions and technical details may have changed since writing.
Eight years ago I wrote a (then) modern guide to implementing off-canvas navigation for Smashing Magazine. My original demo is still online (links in the article are broken). Itās rather quaint by todayās standards.
Back in 2013 responsive design was not yet fully accepted as the norm. Unthinkable! JavaScript was needed to fix the nav element in IE. Between then and now I must have written over a hundred variations of this pattern improving it over time. Iāve often advised against it as it can encourage lazy information architecture.
The New Version
Check out my new demo on CodePen (dbushell).
I think Iāve finally nailed it⦠well I am at least happy with the latest iteration but Iāll continue to tweak it. Iāve listed the key features on the demo page and expanded upon them below. Itās all built with accessibility and performance as a priority.
Progressive Enhancement
The navigation works without JavaScript. Itās set up using the ājump to menuā and āreturn to top of pageā anchor pattern with the <nav> element at the end of the document. Sub-menus are accessible with the aid of :target selectors.
Scroll Snap
CSS Scroll Snap is the magic behind the sub-menus. The sub-menu <ul> elements sit side-by-side with the parent overflow hiding all but the active menu. Each sub-menu link references their target to activate them, e.g. <a href="#menu-2">.
Optional Animations
Because the sub-menus use native browser scrolling, transitions back and forth are added with a single CSS declaration: scroll-behavior: smooth. The prefers-reduced-motion media query is used first to respect user preference.
The main open & close off-canvas animation uses JavaScript to toggle various CSS transform transitions. My example (dbushell) has some opinionated design to demonstrate effects like overlay transparency. For simplicity, the no-JS default contains no motion.
Accessible Markup
Iāve taken care to use appropriate elements and ARIA attributes. To the best of my research and testing Iāve implemented this correctly. Feedback is very welcome @dbushell (@db).
Focus State and Keyboard Navigation
Iām all about focus state and focus-visible means no more weak āugly designā excuses. Generally speaking, using a pointer ā mouse, finger, etc ā leaves no focus outline; keyboard navigation does. Ultimately itās for the browser to decide. The āEscapeā key can be used to close the menu. To trap and restore focus Iāve borrowed a technique from Kitty Giraudelās fantastic A11y Dialog (KittyGiraudel/a11y-dialog).
Right-to-Left Styles
RTL styling is supported out of the box. CSS logical properties and values make this fairly trivial to provide. The document attribute <html dir="rtl"> is used to change the JavaScript animation. A reasonable requirement, I think?
You could, of course, partially invert the design regardless of language direction. I feel like most LTR designs I see nowadays use a right-hand-side menu but itās not a hard rule.
Sticky Header
The demo features a bonus āsticky headerā technique. Thatās a common complimentary pattern and easy to do efficiently with an Intersection Observer.
Browser Support
Modern browsers are very good. Unfortunately Safari is a step behind them. Iāll save my PWA rant for another day; my off-canvas navigation works fine in Safari. There are a few niceties that require JavaScript polyfills (scroll-behavior and focus-visible). Personally Iād opt to avoid unnecessary JavaScript. In this case the polyfills are lean enough that I wouldnāt put up much of a fight if project stakeholders questioned it.
Plugin? React?
No āpluginā Iām afraid. An MIT licensed CodePen demo (dbushell) is all Iām maintaining! The nuances are too varied to provide a configurable plugin. Doing so would bloat the code with more settings than actual functionality and make it less adaptable. I might work on a few design variations to show how flexible it can be.
Thereās nothing that couldnāt be reimplemented in component frameworks like React or Svelte. No DOM nodes are added or removed. Only attributes and temporary styles are modified. If the components are only rendered once you could run the JavaScript as-in after everything is mounted. Or go ahead and fully integrate it to bind elements etc! Iām guessing if you know those frameworks my JavaScript should be simple.
Like I said Iām still working on this and constructive feedback is welcome @dbushell (@db)!