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:
- Select Window > Package Management > Package Manager to open the Package Manager.
- Select the Built-in section.
- Select the Accessibility module.
- Select Enable.
Create the button
Use UI Toolkit to create a Start Game button in your scene.
-
Create a project with any template.
-
Create a UXML file namedwith the following content:
AccessibleStartMenu.uxml<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> -
Create a C# script namedwith the following content:
AccessibleStartMenu.cs
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 components or UI elements directly. They rely on this hierarchy to navigate your application. You create an , then add an that represents the Start Game button.
GameObjectAccessibilityHierarchyAccessibilityNodeTo create the accessibility hierarchy:
- Add the namespace.
UnityEngine.Accessibility - Create an instance.
AccessibilityHierarchy - Create and add an to the accessibility hierarchy.
AccessibilityNode - Set the ,
label, androleproperties of the node according to the button's text and interactable state.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:
- Track the button's changes in size and position.
- Calculate its screen coordinates from its world coordinates and the UI scale factor.
- Set the node's property to the calculated screen rectangle.
frame
Update the script as below:
AccessibleStartMenu.cspublic 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 event, which is triggered when the user activates the node via the screen reader, then invoke the button's in the event handler.
invokedNavigationSubmitEventUpdate the method in the script as below:
CreateAccessibilityHierarchyAccessibleStartMenu.cspublic 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
- When the menu appears, activate the accessibility hierarchy by assigning it to .
AssistiveSupport.activeHierarchy- When the user turns the screen reader off, is automatically set to
AssistiveSupport.activeHierarchyto free resources.null
- When the user turns the screen reader off,
- Re-assign the hierarchy every time the user turns the screen reader on.
- When the menu disappears, remove the hierarchy by setting to
AssistiveSupport.activeHierarchy.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 () of the visual button that screen readers can discover and interact with.
AccessibilityNodeThe complete script is as follows:
AccessibleStartMenu.csusing 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:
- Create an empty in your scene and name it
GameObject.AccessibleStartMenu - Add a component to the
UI Document.GameObject - Create a Panel Settings Asset and assign it to the field in the Inspector window of the
Panel Settingscomponent.UI Document - Assign the file to the
AccessibleStartMenu.uxmlfield.Source Asset - Add the script to the
AccessibleStartMenu.cs.GameObject
Test the hierarchy and node properties in Play mode
To test the hierarchy and node properties in the Unity Editor:
- Enter Play mode.
- Select Window > Accessibility > Hierarchy Viewer.
- 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:
-
Build and run the application on your target platform (Android, iOS, Windows, or macOS).
-
Get familiar with the gestures or commands of your platform's built-in screen reader:
- Android: TalkBack gestures on Android
- iOS: VoiceOver gestures on iPhone
- Windows: Narrator commands on Windows
- macOS: VoiceOver commands on Mac
-
Enable the screen reader.
-
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".
-
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
- 📚 Documentation:Accessibility module API reference
- 📺 Video:Reach new audiences with Accessibility and Localization in Unity 6 (Unite 2025)
- ⚙️ Sample project:LetterSpell: example of an accessible Unity application
- 👥 Community:Unity Discussions: Accessibility