Set accessibility role
On Android, you can use the setAccessibilityDelegate) method of ViewCompat to get a reference to AccessibilityNodeInfoCompat. This object contains many useful accessibility related methods.
You can set a role using the setRoleDescription) method. However, we recommend using the setClassName) method over setRoleDescription to support multilingual roles. For example, set Button::class.java.name if an element behaves like a button. The role will be set to Button in English, and to its respective translation in other languages.
|Element type | Class name | |---------------|------------------------------| |Button | android.widget.Button | |Checkbox | android.widget.CompoundButton| |Drop down list | android.widget.Spinner | |Edit box | android.widget.EditText | |Image | android.widget.ImageView | |Toggle button | android.widget.ToggleButton | |Radio button | android.widget.RadioButton | |Progress bar | android.widget.ProgressBar | |Value picker | android.widget.NumberPicker |
To indicate other element types, such as Switch or Tab, use setRoleDescription.
You can indicate a heading by using the setHeading) method. ViewCompat also contains a convenience method: setAccessibilityHeading).
kotlin
ViewCompat.setAccessibilityDelegate(
element,
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
info: AccessibilityNodeInfoCompat
) {
super.onInitializeAccessibilityNodeInfo(host, info)
// Button
info.className = Button::class.java.name
// Image
info.className = ImageView::class.java.name
// Heading
info.isHeading = true
// Custom
info.roleDescription = "Custom role"
}
}
)
// Convenience method
ViewCompat.setAccessibilityHeading(view, true) kotlin
if (ClassLoadingCache.checkInstanceOf(className, android.widget.EditText.class)) {
if (node.isEnabled() && !node.isEditable()) {
// Developers may want to provide extra information
// when an EditText is enabled but not editable.
return ROLE_NONE;
} else {
return ROLE_EDIT_TEXT;
}
} kotlin
ViewCompat.setAccessibilityDelegate(
element,
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View,
info: AccessibilityNodeInfoCompat
) {
super.onInitializeAccessibilityNodeInfo(host, info)
// EditText
info.className = EditText::class.java.name
info.isEditable = true
}
}
)
// Convenience method
ViewCompat.setAccessibilityHeading(view, true) In Jetpack Compose you can use role to set the role of an element.
The following constants are defined for the Role:
Button): this element is a buttonCheckbox): this element is checkbox with two states (checked / unchecked)DropdownList): this element is drop down menuImage): this element is an imageRadioButton): this element is a radio buttonSwitch): this element is a switchTab): this element is a tab which represents a single page of content using a text label and/or iconValuePicker: this element is a value picker and should support accessibility scroll events
For more information about the mapping, view the Role class, Role.toLegacyClassName() method and populateAccessibilityNodeInfoProperties() method in the Compose source code.
kotlin
Box(modifier = Modifier.semantics {
role = Role.Button
}) On iOS, the accessibilityTraits attribute is used to indicate an accessibility role. The UIAccessibilityTraits structure contains all options, such as header, button, link and image, among others.
You can also combine multiple traits. For example, for a selected button you can can pass both traits as an array: [.button, .selected].
swift
element.accessibilityTraits = .button
element.accessibilityTraits = .header
element.accessibilityTraits = .link
element.accessibilityTraits = .image
element.accessibilityTraits = [.button, .selected] In SwiftUI, you can set an accessibility role by using the accessibility traits on views. This is done by using the accessibilityAddTraits) modifier, which allows you to specify the role a view should play in the user interface. Traits can make a view act as a button, header, link, image, and more. You can also combine multiple traits to define complex roles.
swift
// Button Trait
Text("Tap Me")
.accessibilityAddTraits(.isButton)
// Header Trait
Text("Section Header")
.font(.headline)
.accessibilityAddTraits(.isHeader)
// Link Trait
Text("Visit Website")
.foregroundColor(.blue)
.underline()
.accessibilityAddTraits(.isLink)
// Image Trait
Image(systemName: "star.fill")
.accessibilityAddTraits(.isImage)
// Combined Traits: Button and Selected
Text("Selected Button")
.accessibilityAddTraits([.isButton, .isSelected]) swift
// View containing an image that acts as a decorative element
Image(systemName: "envelope")
.accessibilityRemoveTraits(.isImage) For some widgets in Flutter, the role is assignd automatically. This happens, for example, with Flutter's buttons and text fields. If this is not the case, you can use Semantics to indicate a role. The Semantics constructor contains all available options, such as button, header, link and image, among others.
dart
Semantics(
button: true,
header: true,
link: true,
image: true,
child: Widget(...)
); In React Native you can use the accessibilityRole prop to set the accessibility role of an element. Available roles include button, header, link and image, among others.
jsx
<Pressable
accessibilityRole="button|header|link|image" /> In MAUI, there is no built-in support for setting an accessibility role.
By intercepting the handler changed event, you can change the role of a custom component.
HandlerChanged event in XAML:
xml
<StackLayout>
<BindableLayout.ItemTemplate>
<DataTemplate>
<controls:BorderedFrame
HandlerChanged="Frame_HandlerChanged">
<Grid...>
</Grid>
<Frame.GestureRecognizers>
<TapGestureRecognizer/>
</Frame.GestureRecognizers>
</controls:BorderedFrame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout> csharp
public partial class Component
{
void Frame_HandlerChanged(System.Object sender, System.EventArgs e)
{
if (sender is Frame frame && frame.Handler?.PlatformView is Android.Widget.FrameLayout view)
{
ViewCompat.SetAccessibilityDelegate(view, new CustomFrameDelegate(ViewCompat.GetAccessibilityDelegate(view)));
}
}
}
public class CustomFrameDelegate : AccessibilityDelegateCompatWrapper
{
public CustomFrameDelegate(AccessibilityDelegateCompat? originalDelegate) : base(originalDelegate)
{
}
public override void OnInitializeAccessibilityNodeInfo(Android.Views.View host, AccessibilityNodeInfoCompat info)
{
base.OnInitializeAccessibilityNodeInfo(host, info);
if (info != null)
info.ClassName = "android.widget.Button";
}
} csharp
public partial class Component
{
void Frame_HandlerChanged(System.Object sender, System.EventArgs e)
{
if (sender is Frame frame && frame.Handler != null)
{
var view = (UIView)frame.Handler.PlatformView!;
view.AccessibilityTraits = UIAccessibilityTrait.Button;
}
}
} Xamarin Forms does not have built-in support for setting an accessibility role.
By using Effects it is possible to implement platform specific behaviour.
The SemanticEffect file inside the Xamarin.CommunityToolkit defines various methods to set accessibility roles.
xml
<controls:CustomFontLabel
xct:SemanticEffect.HeadingLevel="1"
xct:SemanticEffect.Description="Button" /> Indicate accessibility modal
On Android, there is no method to indicate an accessibility modal. However, you can indicate an accessibility pane by using the setPaneTitle) method. ViewCompat also contains a convenience method: setAccessibilityPaneTitle). Please keep in mind that focus is not trapped when a pane title has been set.
kotlin
ViewCompat.setAccessibilityPaneTitle(view, "Appt pane") In Jetpack Compose, to set a title for an accessibility pane, you can use paneTitle.paneTitle()) inside semantics.semantics(kotlin.Boolean,kotlin.Function1)) block modifier.
It's recommended to set the pane title for high-level layouts, such as Scaffold).
Please keep in mind that focus is not trapped when a pane title has been set.
kotlin
Scaffold(modifier = Modifier.semantics {
paneTitle = "Appt pane"
}) { paddingValues ->
// Scaffold content...
} On iOS, you can indicate an accessibility modal by using the accessibilityViewIsModal property.
swift
viewController.accessibilityViewIsModal = true In SwiftUI, you can indicate an accessibility modal by adding .isModal to accessibilityTraits.
Use this trait to control which accessibility elements can be accessed by assistive technology. When a modal accessibility element is active, other sibling elements that are not part of the modal are hidden from assistive technologies.
Note: When designing a custom modal view in SwiftUI, it’s crucial to include a cancel or dismiss button. This button provides users with a clear and easy way to exit the modal without completing any actions.
swift
@State private var isModalPresented = false // State to control the modal presentation
var body: some View {
ZStack {
Button("Show Modal") {
isModalPresented = true // Present the modal
}
if isModalPresented {
EmptyModalView(isPresented: $isModalPresented)
.frame(width: 200, height: 200)
.accessibilityAddTraits(.isModal) // Add .isModal trait
}
}
} On Flutter, the ModelBarrier class takes accessibility into account. The barrierDismissable and barrierLabel are used by assistive technologies. When barrierDismissable is set to false, the focus of assistive technologies is trapped inside the modal. The value of barrierLabel is announced upon entering the modal.
dart
showDialog(
context: context,
barrierDismissible: false,
barrierLabel: 'Label'
builder: (context) {
return SimpleDialog(
title: Text('Appt')
);
},
); With React Native, you can use the accessibilityViewIsModal prop to mark an accessibility modal. This prop only works on iOS.
jsx
<Modal accessibilityViewIsModal={true}>
<Text>Appt</Text>
</Modal> In MAUI, you can use two approaches to display modals:
- MAUI's default
Navigation.PushModalAsync. More detailshere.
csharp
Navigation.PushModalAsync(new ModalPageToDisplay()); csharp
var popup = new SimplePopup();
rootPage.ShowPopup(popup); Xamarin Forms does not have built-in support to indicate an accessibility modal.
xml
Not available, contribute! Group elements
On Android you can group elements by using the android:focusable and android:screenReaderFocusable attributes. Sometimes you also need the android:importantForAccessibility attribute. Don't for get to set an android:contentDescription for the group.
Keep in mind that android:focusable is not only used by assistive technologies, but also by other means of interaction.
xml
<LinearLayout
android:focusable="true"
android:screenReaderFocusable="true"
android:contentDescription="Appt group">
<TextView
android:focusable="false"
android:importantForAccessibility="no"/>
<ImageView
android:focusable="false"
android:importantForAccessibility="no"/>
</LinearLayout> In Jetpack Compose, you can group elements by using mergeDescendants.semantics(kotlin.Boolean,kotlin.Function1)). This will group all elements within this parent element and they will be focused and read out together.
kotlin
// Merge all semantics of box elements
Box(modifier = Modifier
.semantics(mergeDescendants = true) { }
) {
// Box content ...
} On iOS, you can group elements by setting isAccessibilityElement to true on the parent element. Don't forget to set an accessibilityLabel for the group.
Sometimes it can be useful to also the shouldGroupAccessibilityChildren property to group the accessibility elements that are children of the element, regardless of their positions on the screen.
swift
group.isAccessibilityElement = true
group.shouldGroupAccessibilityChildren = true
group.accessibilityLabel = "Appt group" In SwiftUI, you can group multiple elements by applying the .accessibilityElement(children:)) modifier with the .combine option to a parent view. This approach consolidates the accessibility elements into a single group.
You can optionally override the combined description of the children with a custom description for the group by setting an accessibilityLabel-1d7jv).
swift
@State var stockPrice = "$123.45"
var body: some View {
HStack {
Text("Stock price:")
Text(stockPrice)
}
// Combine all text elements into a single accessibility element
.accessibilityElement(children: .combine)
.accessibilityLabel("The stock price is: \(stockPrice)")
} On Flutter, there are multiple types of semantics to group accessibility elements. The excludeSemantics property can be used to override the semantics of all children. You can achieve similar behavior by using BlockSemantics.
dart
Semantics(
label: 'Appt group',
excludeSemantics: true,
child: Column(
children: [
Text('Appt'),
Text('is a platform for accessibility')
]
)
); In React Native, you can group elements together by using the accessible prop. An accessibilityLabel should be set for grouped elements.
Note: all touchable elements are accessible by default.
jsx
<View accessible accessibilityLabel="Appt group">
<Text>Appt</Text>
<Text>is a platform for accessibility</Text>
</View> In MAUI, you can group items together by setting AutomationProperties.IsInAccessibleTree to true. You also need to set a SemanticProperties.Description for the grouped elements.
xml
<HorizontalStackLayout
x:Name="MenuButtonLayout"
AutomationId="MenuButton"
AutomationProperties.IsInAccessibleTree="True"
SemanticProperties.Description="Menu container"
Padding="0,0,5,0">
<Image
x:Name="MenuImage"
HeightRequest="20"
HorizontalOptions="Start"
SemanticProperties.Description="Menu image"
Source="dotnet_bot.png"
WidthRequest="20" />
<Label
x:Name="MenuLabel"
Margin="5,0,0,0"
Text="Menu label"
VerticalTextAlignment="Center" />
<HorizontalStackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_OnTapped" />
</HorizontalStackLayout.GestureRecognizers>
</HorizontalStackLayout> Xamarin Forms does not have built-in support to group accessibility elements.
csharp
Not available, contribute!