@murmurcreative/site-navigation

An HTML Custom Element to make site nav a bit easier.

Usage no npm install needed!

<script type="module">
  import murmurcreativeSiteNavigation from 'https://cdn.skypack.dev/@murmurcreative/site-navigation';
</script>

README

πŸ—ΊοΈ 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
// main.js, or a script that will be run on all pages
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:

// resources/assets/build/webpack.config.js
test: /\.js$/,
exclude: [/node_modules(?![/|\\](bootstrap|foundation-sites))/],

//...to this:

test: /\.js$/,
exclude: [/node_modules(?![/|\\](bootstrap|foundation-sites|site-navigation))/],

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:

  1. Your Drawer can be any block-level element, but it must meet at least one of the following criteria:
    1. Have the attribute data-drawer
    2. Directly follow a Toggle
  2. 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
// Let's assume you have a Drawer with the ID `location-list`, which is currently closed.

document.getElementByID(`location-list`).toggleDrawer();
// The Drawer is now open.

document.getElementByID(`location-list`).openDrawer();
// The state of the Drawer has not changed.

document.getElementByID(`location-list`).closeDrawer();
// The Drawer is now closed.

document.getElementByID(`location-list`).openDrawerSilently();
// The drawer is now open, but the parent <site-navigation> was 
// not notified.

Toggles

Toggles are the buttons used to open and close Drawers. They must obey the following rules:

  1. Have the attribute data-toggle
  2. Directly preceed a Drawer
  3. 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).