A canvas is a picture, and the hardest thing on the web to make usable without one. Hakushi makes each part of it reachable and named, and says what happens.

The name of a canvas

Give each canvas a name: label="The tables of the shop". The canvas has the application role, so that a screen reader passes the keys to it, with "Canvas" as its role description, in the canvas's language.

Nodes

Each node is a group with "node" as its role description, named by node_label (its data's label or name, or its id, unless you give your own). When a node gets the focus, its description says its connections: "2 connections: → users, ← invoices (paid by)".

The focus goes from node to node with Tab, and to the nearest node in a direction with Alt and an arrow; a node that gets the focus comes into view, culled or not. Enter goes into a node's fields and buttons, which are the page's own, and Escape back to the node. See Navigation.

Connecting without a pointer

C, on a focused node, starts an edge from it. The arrows go through the nodes it may end on (by the same rules as a pointer's, nearest first), each said as it comes ("Connect to users · id"), and Enter makes the edge. Every edge a pointer can draw, the keyboard can.

What is said

A live region says, politely:

  • a selection ("users selected.", "3 selected.");
  • a move ("Moved users.", "Moved 3 nodes.");
  • a delete, a connection, a cancelled connection;
  • a change the server refused, in its words;
  • no connection to the server.

The words are the canvas's own (Hakushi.Messages), in the canvas's locale.

Handles

A handle has the label you give it, for the keyboard's connection choices: give the handles of a row the row's name.

The focus ring

The canvas and its nodes show a focus ring when the focus came from the keyboard, and not after a click.

Colour and motion

Selection is a ring, not a colour alone. With forced colours, the canvas uses the system's; with reduced motion, nothing moves on its own. See Theming.

What is not done yet

The edges themselves do not take the focus: they are read through their nodes, and deleted with the nodes or by a pointer. See Known limits.