Welcome to the Xiao demo page

Xiao is a small, accessible, framework agnostic, browser-driven routing system. Make single page applications with progressive enhancement. This demo is an incomplete exploration of Xiao. See the Github repository for more.

This is the default route, home, and just contains static content, loaded with the web page from the server. Note the id="home" attribution in the HTML:

<div id="home">
  <h1>A demo built with Xiao</h1>
  <p>Xiao is a small, accessible, framework agnostic, browser-driven routing system. Make single page applications with progressive enhancement and without the complexity.</p>
  <p>This is the default route, <code>home</code>, and just contains static content, loaded with the web page from the server. Note the <code>id="home"</code> attribution in the HTML:</p>
</div>

To make Xiao recognize this element as a route element, you add it to a routes array when you instantiate the app.

var app = new Xiao([{ id: 'home', label: 'Home'  }], 'home')

The second argument defines the default route, which will be activated if no route hash is provided in the web address on page load.

Accessibility semantics

Route elements are given assistive technology-accessible information when a Xiao app is initialized. Note role="region" and aria-label="Home" (the value of which is taken from either the route element's first heading or the route object's label property):

<div id="home" role="region" aria-label="Home">
  <h1>A demo built with Xiao</h1>
  <p>Xiao is a small, accessible, framework agnostic, browser-driven routing system. Make single-page applications with progressive enhancement and without the complexity.</p>
  <p>This is the default route, <code>home</code>, and just contains static content, loaded with the web page from the server. Note the <code>id="home"</code> attribution in the HTML:</p>
</div>

Focus management

Whenever the hash changes in a Xiao routed app's URL, Xiao determines if it corresponds to a route or an element inside a route. If so, it will move focus to that element. This will move keyboard users into the correct position, and elicit the announcement of accessibility information in screen readers. For example: "main, region, home".

Try opening developer tools, clicking this Subsections link, then entering document.activeElement into your console. The active element is the focused element.

Route lifecycles

Routes have a lifecycle that begins with the user's arrival at the route and ends with their departure. You can tap into these lifecycle events with the arrived and departed methods.

For example, I can record the arrival time as a timestamp and write it to the page, like this:

id: 'lifecycle',
label: 'Route lifecycle',
arrived(elem) {
  var time = Date.now();
  var timeElem = elem.querySelector('.timestamp')
  timeElem.textContent = `Route arrival timestamp: ${time}`
}

If you refresh the page, the timestamp updates. It also updates when you move to another route and come back. Obviously, this is a silly example. You're more likely to use the arrived method to load XHR content and/or compile some templates. That sort of thing.

The departed method

The departed method fires when the user leaves the route. One use may be to save some route-related data to localStorage. Below, you should see the message "Welcome!" if it is your first time at this route or "Welcome back!" if you've been here before.

We set the storage item on departed

departed(elem) {
  window.localStorage.setItem('visited', true)
}

…and retrieve it on arrived.

arrived(elem) {
  var visited = window.localStorage.getItem('visited')
  var msg = visited ? "Welcome back!" : "Welcome!"
  elem.querySelector('.welcome').textContent = msg
}

(Note that the departed function only executes when the page's URL changes, not when the page is reloaded.)

Parameters

In these examples, we just used the elem parameter. There are three parameters you can supply to the lifecycle functions in total. In order:

Subsections

When you link to a subsection from within a route that is already active, it just behaves as a basic hash fragment and no JavaScript is fired.

A subsection

Alternatively, if you link to a subsection in a different route, like the subsection about focus management on the Home page, the route is revealed, events and methods fired, and the subsection brought into view.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque dignissim molestie diam. Integer fermentum nisi eget vehicula tristique. Vivamus leo justo, pharetra quis euismod ac, vestibulum non neque. Ut eget est sem. Ut quis ultricies mauris. Donec mauris tortor, posuere non mi ac, accumsan rutrum sapien.

Phasellus suscipit libero blandit sapien feugiat, vel mattis velit venenatis. Aenean eu ligula viverra tellus accumsan cursus ut et lorem. Donec non laoreet ex. In vitae turpis felis. Nam sollicitudin sit amet orci et hendrerit. Nulla congue dictum finibus.

Ut ut ligula porttitor purus dictum faucibus. Aliquam suscipit dui in augue pharetra, sed pellentesque dolor fringilla. Donec ornare est eget sapien tristique rutrum. Nulla facilisi. Praesent nec euismod leo. Morbi nunc dui, rhoncus quis massa ut, mollis gravida mauris. Praesent quis nisl ut ipsum ultrices mollis. Vivamus at ex orci.

Using params

The point of single-page applications is that different routes have access to the same working data. But sometimes you'll want to pass specific pieces of data between routes, based on user choices. This is what query strings are for. Just add them in the URL of a route link:

<a href="?up=down&left=right#params">params</a>

Xiao converts the query string into an object and makes it available in the route object's arrived and departed methods.

{
  id: 'params',
  arrived(elem, params, routes) {
    var paramsElem = elem.querySelector('.params-object')
    paramsElem.textContent = JSON.stringify(params)
  }
}

Try changing the params in the URL and refreshing the page to affect the live result below: