Heydon / xiao

A tiny, accessible, browser-driven single-page routing system.
The Unlicense
56 stars 2 forks source link

Xiao

xiǎo: small, young

Xiao (pronounced "Shh!" + "Ow!") is a small, accessible, framework agnostic, dependency free, browser-driven routing system. Make single-page applications with progressive enhancement. See the docs folder for the working demo, or view it here.

Install

npm i xiao-router

Include

Xiao is just a small script you include in your web page. You have the option of using the ES5 or slightly smaller but less well-supported ES6 version.

ES5

<script src="https://github.com/Heydon/xiao/raw/master/./node_modules/xiao-router/xiao-es5.min.js"></script>

ES6

<script src="https://github.com/Heydon/xiao/raw/master/./node_modules/xiao-router/xiao.min.js"></script>

Routes

In Xiao, routes are just subdocuments of web pages with metadata and (sometimes) methods attached to them. Each is identified by an id which corresponds to both a page element (say id="home") and a hash fragment (say #home).

Before initializing your Xiao app, you define a routes array. Only the route id is mandatory.

const routes = [
  {
    id: 'home'
  },
  {
    id: 'about'
  },
  {
    id: 'upload'
  }
]

This routes array is supplied as the first argument of the instantiated Xiao app. The default route — the one the user is directed to when hitting the root of the application — is the second mandatory argument.

const app = new Xiao(routes, 'home')

Whether written by hand or via templating, each route is just an element with an id in an HTML document:

<div id="home">
  <!-- the (initially) static content of the route -->
</div>

On initialization, Xiao gives each element corresponding to a route role="region" then calculates a value for an aria-label, to further identify the route to assistive technologies. The label is:

<div id="about" role="region" aria-label="About my project">
  <!-- the (initially) static content of the route -->
</div>

Traversing a Xiao routed app

When a user navigates to a hash fragment, Xiao determines if that hash fragment either

In default operation, whether you navigate to a route element or a route child element, the previous route element is hidden and the new one revealed. For keyboard accessibility, focus is sent to the newly revealed route element.

Current links

Links corresponding to currently active routes receive the aria-current="true" attribution:

<a href="#home" aria-current="true">Home</a>

This identifies current links to assistive technologies and doubles as a styling hook where desired.

nav [aria-current] {
  border-bottom: 2px solid;
}

The <title>

It is recommended that the <title> value you supply is the name of the app. Xiao appends the label for the current route after a separator.

<title>My App | Home</title>

The arrived and departed methods

You can hook into lifecycle events for routes to perform operations. In Xiao, these are named arrived and departed. You simply add them as properties on the route object.

{
  id: 'about',
  label: 'About my project'.
  arrived(elem, params, routes) {
    // Add a class, pull in some dynamic content, whatever
  },
  departed(elem, params, routes) {
    // Save some settings, remove some content, whatever
  }
}

As you can see, there are three parameters available in each case:

Events

reroute

Whenever a new route is invoked, the reroute event is dispatched from window. This allows you to affect any part of the page in response to a reroute. Secreted in this CustomEvent's details object are the old and new route objects.

window.addEventListener('reroute', e => {
  console.log('Old route:', e.detail.oldRoute)
  console.log('New route:', e.detail.newRoute)
})

Rerouting programmatically

Xiao capitalizes on standard browser behavior, letting you use links and hash fragments to invoke routes. However, there will be times you want to reroute the user programmatically. A redirect maybe. For this, you can use the reroute method.

The first argument is the desired route (or route child element) id and the second any params you may want to supply.

app.reroute('login', '?redirect=true')

Settings

The third (optional) argument when instantiating a Xiao app is the settings object. These are the options:

Framework independence

Xiao is just a simple router which respects browser standards. The actual functionality you provide within individual Xiao routes is totally up to you. You can use plain JavaScript, React or Vue components, whatever you like. With Xiao, simple single-page applications can be just that: simple. But you can add as many dependencies and as much code to a Xiao skeleton as you like.