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:
-
A component calls
setAccessibilityfor each element that has a meaning. It gives a role, a name, and a state. - 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.
- Knots calculates a digest of the snapshot. The snapshot revision changes only when the meaning changes, not when pixels change.
- 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
Buttonuses itslabelas its name. A button without a label has no name. -
A
Checkboxuses itslabel. Always set one. -
A
TextInputhas no label field. Put aTextlabel 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 theui.Contextbefore the next frame.
A host with its own accessibility system can read the snapshot and give actions to
ui.Context.enqueueAccessibilityAction.