Documentation

Unity Engine


User Manual

Script Reference

Unity Engine


Get started with screen reader support

Use the screen reader support APIs to make your first accessible button.
Read time 7 minutesLast updated 13 days ago

The screen reader support APIs are agnostic of the UI system, so they work with UI Toolkit, uGUI, custom UI frameworks, and non-UI content such as 2D or 3D objects in the game world. For simplicity, this guide uses UI Toolkit, but you can adapt the code to your UI framework of choice.

Example overview

This example illustrates how to create an accessibility node, connect it to a UI Toolkit button, and test it with platform screen readers. By the end, you'll have a button that native screen readers can read and activate.

Prerequisites

This guide is for developers familiar with the Unity Editor, UI Toolkit, and C# scripting. Before you start, get familiar with the following:

Enable the Accessibility module

The Accessibility module is enabled by default. If for some reason it's not enabled in your project, do the following to enable it:
  1. Select Window > Package Management > Package Manager to open the Package Manager.
  2. Select the Built-in section.
  3. Select the Accessibility module.
  4. Select Enable.

Create the button

Use UI Toolkit to create a Start Game button in your scene.
  1. Create a project with any template.
  2. Create a UXML file named
    AccessibleStartMenu.uxml
    with the following content:
    <ui:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False"> <ui:Button text="Start Game" name="startButton"/></ui:UXML>
  3. Create a C# script named
    AccessibleStartMenu.cs
    with the following content:
using UnityEngine; using UnityEngine.UIElements; public class AccessibleStartMenu : MonoBehaviour { Button m_Button; void OnEnable() { VisualElement root = GetComponent<UIDocument>().rootVisualElement; m_Button = root.Q<Button>("startButton"); m_Button.clicked += OnButtonClicked; } void OnDisable() { m_Button.clicked -= OnButtonClicked; } void OnButtonClicked() { Debug.Log("Start Game button clicked"); } }

Create the accessibility hierarchy

The accessibility hierarchy is a semantic representation of your UI that screen readers use to discover and interact with your content. Screen readers cannot detect
GameObject
components or UI elements directly. They rely on this hierarchy to navigate your application. You create an
AccessibilityHierarchy
, then add an
AccessibilityNode
that represents the Start Game button.
To create the accessibility hierarchy:
  1. Add the
    UnityEngine.Accessibility
    namespace.
  2. Create an
    AccessibilityHierarchy
    instance.
  3. Create and add an
    AccessibilityNode
    to the accessibility hierarchy.
  4. Set the
    label
    ,
    role
    , and
    state
    properties of the node according to the button's text and interactable state.
// ...using UnityEngine.Accessibility;public class AccessibleStartMenu : MonoBehaviour{ // ... AccessibilityHierarchy m_AccessibilityHierarchy; AccessibilityNode m_AccessibilityNode; void OnEnable() { // ... CreateAccessibilityHierarchy(); } // ... void CreateAccessibilityHierarchy() { // Create a new accessibility hierarchy. m_AccessibilityHierarchy = new AccessibilityHierarchy(); // Create a new accessibility node with the button's text as the label // (what the screen readers announces). m_AccessibilityNode = m_AccessibilityHierarchy.AddNode(m_Button.text); // Set a semantic role (tells the screen reader this is a button). m_AccessibilityNode.role = AccessibilityRole.Button; // Set the state (is it currently interactable?). m_AccessibilityNode.state = m_Button.enabledSelf ? AccessibilityState.None : AccessibilityState.Disabled; }}

Set the node's screen coordinates according to the button's size and position

To set the node's screen coordinates:
  1. Track the button's changes in size and position.
  2. Calculate its screen coordinates from its world coordinates and the UI scale factor.
  3. Set the node's
    frame
    property to the calculated screen rectangle.
Update the
AccessibleStartMenu.cs
script as below:
public class AccessibleStartMenu : MonoBehaviour{ // ... void OnEnable() { // ... m_Button.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged); } void OnDisable() { // ... m_Button.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged); } // ... void OnGeometryChanged(GeometryChangedEvent evt) { Rect worldRect = m_Button.worldBound; float scale = m_Button.panel.scaledPixelsPerPoint; // Update the screen coordinates of the node. m_AccessibilityNode.frame = new Rect(worldRect.position * scale, worldRect.size * scale); }}

Connect the node's activation event to the button

Subscribe to the node's
invoked
event, which is triggered when the user activates the node via the screen reader, then invoke the button's
NavigationSubmitEvent
in the event handler.
Update the
CreateAccessibilityHierarchy
method in the
AccessibleStartMenu.cs
script as below:
public class AccessibleStartMenu : MonoBehaviour{ // ... void CreateAccessibilityHierarchy() { // ... // Handle when the user activates this node (e.g., double-tap). // Called `selected` in versions before Unity 6.3. m_AccessibilityNode.invoked += () => { using var evt = NavigationSubmitEvent.GetPooled(); evt.target = m_Button; m_Button.SendEvent(evt); return true; }; }}

Activate the accessibility hierarchy when a screen reader is enabled

  1. When the menu appears, activate the accessibility hierarchy by assigning it to
    AssistiveSupport.activeHierarchy
    .
    • When the user turns the screen reader off,
      AssistiveSupport.activeHierarchy
      is automatically set to
      null
      to free resources.
  2. Re-assign the hierarchy every time the user turns the screen reader on.
  3. When the menu disappears, remove the hierarchy by setting
    AssistiveSupport.activeHierarchy
    to
    null
    .
public class AccessibleStartMenu : MonoBehaviour{ // ... void OnEnable() { // ... AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy; AssistiveSupport.screenReaderStatusChanged += OnScreenReaderStatusChanged; } void OnDisable() { // ... AssistiveSupport.activeHierarchy = null; AssistiveSupport.screenReaderStatusChanged -= OnScreenReaderStatusChanged; } // ... void OnScreenReaderStatusChanged(bool enabled) { if (enabled) { AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy; } // else // { // // This is automatically done when the user turns the screen // // reader off. // AssistiveSupport.activeHierarchy = null; // } }}
You created a semantic representation (
AccessibilityNode
) of the visual button that screen readers can discover and interact with.
The complete
AccessibleStartMenu.cs
script is as follows:
using UnityEngine;using UnityEngine.Accessibility;using UnityEngine.UIElements;public class AccessibleStartMenu : MonoBehaviour{ Button m_Button; AccessibilityHierarchy m_AccessibilityHierarchy; AccessibilityNode m_AccessibilityNode; void OnEnable() { VisualElement root = GetComponent<UIDocument>().rootVisualElement; m_Button = root.Q<Button>("startButton"); m_Button.clicked += OnButtonClicked; m_Button.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged); CreateAccessibilityHierarchy(); AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy; AssistiveSupport.screenReaderStatusChanged += OnScreenReaderStatusChanged; } void OnDisable() { m_Button.clicked -= OnButtonClicked; m_Button.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged); AssistiveSupport.activeHierarchy = null; AssistiveSupport.screenReaderStatusChanged -= OnScreenReaderStatusChanged; } void CreateAccessibilityHierarchy() { // Create a new accessibility hierarchy. m_AccessibilityHierarchy = new AccessibilityHierarchy(); // Create a new accessibility node with the button's text as the label // (what the screen readers announces). m_AccessibilityNode = m_AccessibilityHierarchy.AddNode(m_Button.text); // Set a semantic role (tells the screen reader this is a button). m_AccessibilityNode.role = AccessibilityRole.Button; // Set the state (is it currently interactable?). m_AccessibilityNode.state = m_Button.enabledSelf ? AccessibilityState.None : AccessibilityState.Disabled; // Handle when the user activates this node (e.g., double-tap). // Called `selected` in versions before Unity 6.3. m_AccessibilityNode.invoked += () => { using var evt = NavigationSubmitEvent.GetPooled(); evt.target = m_Button; m_Button.SendEvent(evt); return true; }; } void OnGeometryChanged(GeometryChangedEvent evt) { Rect worldRect = m_Button.worldBound; float scale = m_Button.panel.scaledPixelsPerPoint; // Update the screen coordinates of the node. m_AccessibilityNode.frame = new Rect(worldRect.position * scale, worldRect.size * scale); } void OnButtonClicked() { Debug.Log("Start Game button clicked"); } void OnScreenReaderStatusChanged(bool isEnabled) { if (isEnabled) { AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy; } // else // { // // This is automatically done when the user turns the screen // // reader off. // AssistiveSupport.activeHierarchy = null; // } }}

Attach the script

To attach the script to your scene:
  1. Create an empty
    GameObject
    in your scene and name it
    AccessibleStartMenu
    .
  2. Add a
    UI Document
    component to the
    GameObject
    .
  3. Create a Panel Settings Asset and assign it to the
    Panel Settings
    field in the Inspector window of the
    UI Document
    component.
  4. Assign the
    AccessibleStartMenu.uxml
    file to the
    Source Asset
    field.
  5. Add the
    AccessibleStartMenu.cs
    script to the
    GameObject
    .

Test the hierarchy and node properties in Play mode

To test the hierarchy and node properties in the Unity Editor:
  1. Enter Play mode.
  2. Select Window > Accessibility > Hierarchy Viewer.
  3. Verify that the accessibility hierarchy shows the accessibility node with the correct properties.

The Accessibility Hierarchy Viewer displaying the properties of the accessibility node representing the "Start Game" button

Test the screen reader interaction on your target platform

To test screen reader interaction on your target platform:
  1. Build and run the application on your target platform (Android, iOS, Windows, or macOS).
  2. Get familiar with the gestures or commands of your platform's built-in screen reader:
  3. Enable the screen reader.
  4. Navigate to the button using screen reader gestures or commands. The screen reader should be able to focus on the button and announce "Start Game, button".
  5. Activate the button using the screen reader's activation gesture or command. The button should respond, and the text "Start Game button clicked" should appear in the player log.

Additional resources