Skip to content

Guide

Accessibility #

Knots gives the operating system a tree of the interface. Screen readers and other assistive technologies read this tree and send actions to it. The built-in components do this for you.

Platform support #

Knots uses AccessKit to connect to the platform accessibility API. The build downloads a prebuilt AccessKit C library. You do not need Rust.

Platform API Status
macOS NSAccessibility Supported
Windows UI Automation Supported
Linux AT-SPI Supported. Screen positions are relative to the window, because Wayland does not give the window position.
Browser — Not supported yet. The canvas has no accessibility tree.

Accessibility is on by default. Each window has its own adapter. To turn it off, set .accessibility = false in App.Config.

How the tree is made #

In each frame, the components describe their meaning as well as their appearance:

  1. A component calls setAccessibility for each element that has a meaning. It gives a role, a name, and a state.
  2. At the end of the frame, Knots makes an accessibility snapshot. Elements without a meaning, for example layout rows, are not in the snapshot. Their children attach to the nearest ancestor that has a meaning.
  3. Knots calculates a digest of the snapshot. The snapshot revision changes only when the meaning changes, not when pixels change.
  4. The adapter sends only the nodes that changed to the operating system. An animation or a hover effect does not cause updates.

A snapshot can have a maximum of 4096 nodes. Knots converts bounds from logical pixels to physical pixels for the platform.

Roles and state #

Role Used by
button Button, MenuButton, window buttons
checkbox, radio Checkbox, RadioButton, RadioGroup
slider SliderInput
text_input, text_run TextInput, TextArea, Text
select, list_box, list_box_option SelectInput
dialog, menu, tooltip Dialog, FloatingWindow, ContextMenu, Tooltip
generic Other elements that you mark

The state holds disabled, focused, checked, selected, expanded, multiline, text selection, value_text, value_number, min, and max.

Actions #

Assistive technology can send actions: focus, click, collapse, expand, increment, decrement, set_value, replace_selected_text, and set_text_selection.

The adapter puts each action in a queue and wakes the app. The action arrives in the next frame. Components handle it on the same path as pointer and keyboard input. For example, a click action on a button sets clicked in its response. Your code does not need to know if the click came from a mouse, a key, or a screen reader.

The queue holds a maximum of 64 actions.

Give controls good names #

  • A Button uses its label as its name. A button without a label has no name.
  • A Checkbox uses its label. Always set one.
  • A TextInput has no label field. Put a Text label next to it.

Custom components #

A custom interactive component must describe itself. After you open its element, call setAccessibility with the element ID. Then handle actions with consumeAccessibilityAction.

pub fn render(toggle: *const Toggle, frame: *ui.Frame) anyerror!void {
    const ui_state = frame.ui();
    const root: Rect = .{ .key = toggle.key, .style = &toggle_style };
    const id = try root.open(frame);
    try ui_state.setAccessibility(id, .{
        .role = .checkbox,
        .name = toggle.label,
        .state = .{ .checked = toggle.on.* },
    });
    if (ui_state.consumeAccessibilityAction(id, .click) != null) {
        toggle.on.* = !toggle.on.*;
        frame.requestRedraw();
    }
    try root.close(frame);
}

Rect elements are not focusable. For keyboard use, build custom controls from a Button and change its style.

Embedded hosts #

Frame.Output.accessibility holds the snapshot of each frame. A host that owns its window can use knots.NativeAccessibility:

  • create(allocator, io, window_handle, wake_context, wake) makes the adapter.
  • publish(output.accessibility, window_focused) sends the snapshot after each frame.
  • drain(&context) moves queued actions into the ui.Context before the next frame.

A host with its own accessibility system can read the snapshot and give actions to ui.Context.enqueueAccessibilityAction.