🗺️ Site Navigation
An HTML Custom Element to make site nav a bit easier.
Installation
Currently this package is intended to be added to your project with npm/yarn,
and included in your project, likely through webpack. There are other ways you
could include it (running webpack on this repo to generate files in /dist,
for instance), but the following is the recommended process (i.e. the one I've
tested):
$ npm i @murmurcreative/site-navigation
import '@murmurcreative/site-navigation';
If you're using webpack, you'll need to make sure that your loaders for js
and css aren't ignoring the site-navigation directory (often webpack
configuration will ignore node_modules, which will prevent site-navigation
from getting picked up.) As an example, in the default webpack.config.js for
roots' Sage, make the following changes:
test: /\.js$/,
exclude: [/node_modules(?)/],
test: /\.js$/,
exclude: [/node_modules(?)/],
Usage
When used, site-navigation will look at the content you put inside it to
build a menu. An example might look this this:
<site-navigation>
<nav>
<button data-toggle>Open</button>
<div data-drawer>
<ul>
<li>
<a href="/home/">Home</a>
</li>
<li>
<a href="/page-1/">Page One</a>
</li>
<li>
<a href="/page-2/">Page Two</a>
<button data-toggle>Open</button>
<ul>
<li>
<a href="/page-2/page-3/">Page Three</a>
<button data-toggle>Open</button>
<ul>
<li>
<a href="/page-2/page-3/page-4/">Page Four</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</site-navigation>
Note: The above reflects a recommended layout: a <nav> nested in the master
<site-navigation> element, with <div>s as top-level Drawer(s) and <ul>s as nested
Drawers. This is the easiest to understand, and the most accessible. site-navigation
is flexible enough to support many different layouts, however, so long as you follow
the rules below.
Drawers
A Drawer is a section of content that will be hidden and shown when its associated Toggle
is clicked. A Drawer can contain whatever you want it to (even other Drawers!) but it will
usually contain a list of navigational links. The rules for setting up a Drawer are as follows:
- Your Drawer can be any block-level element, but it must meet at least one of the following
criteria:
- Have the attribute
data-drawer
- Directly follow a Toggle
- Your Drawer must be directly preceeded by a Toggle.
Drawers represent the single source of truth about their state: Toggles take their state
from their associated Drawers. This means that if you want to determine or set the state of
a Drawer programmatically, you should do so through the Drawer itself, not a Toggle.
site-navigation only recognizes Drawers and Toggles in pairs: If you have only one, it
will simple ignore that element.
Opening/Closing Drawers Programatically
There are three functions attached to each Drawer which site-navigation uses internally
to handle opening and closing Drawers:
toggleDrawer() - Switches the state of the Drawer.
openDrawer() - Opens the Drawer. Has no effect if the Drawer is already open.
closeDrawer() - Closes the Drawer. Has no effect if the Drawer is already closed.
Each of these functions also has a "silent" variant, which means that the event will not bubble up to the parent event: It will only fire on itself. This can be useful if you're doing something that manipulates drawer state based on the state of another drawer (otherwise you can get a lot of recursion).
Example
document.getElementByID(`location-list`).toggleDrawer();
document.getElementByID(`location-list`).openDrawer();
document.getElementByID(`location-list`).closeDrawer();
document.getElementByID(`location-list`).openDrawerSilently();
Toggles
Toggles are the buttons used to open and close Drawers. They must obey the following rules:
- Have the attribute
data-toggle
- Directly preceed a Drawer
- Be a
<button>
Note: Technically, a toggle can be any element which will dispatch the click event
when clicked, but in practice and for accessibility reasons they should almost always be
<button>s.
Examples
...
<li>
<a href="/contact">Contact Us</a>
<button data-toggle>Open</button>
<ul>...</ul>
</li>
...
👍 Good
...
<li>
<button data-toggle>Open</button>
<a href="/contact">Contact Us</a>
<ul>...</ul>
</li>
...
👎 Bad
The <button> does not immediately preceed the <ul>.
...
<li>
<a href="/contact">Contact Us</a>
<ul>...</ul>
</li>
...
👎 Bad
There is no <button> for the <ul>.
Events
Drawers dispatch events when their state should change, which all other parts of
site-navigation hook into to do their thing. These events bubble up all the way to the
<site-navigation> root element, but stop there (to avoid polluting the wider DOM). You can
listen on the root element or to individual Drawers for the drawer-state-change event.
The detail property on the event includes the element that displatched the event (el)
and the state to which the Drawer is being set (action).