-
Notifications
You must be signed in to change notification settings - Fork 3
Accessibility
Making your app accessible ensures that all users, including those with disabilities, can effectively use your application. Accessibility features work with platform screen readers like VoiceOver (iOS) and TalkBack (Android).
- MAUI SemanticProperties
- MAUI AutomationProperties
- DIPS.Mobile.UI Accessibility Helpers
- Touch Effect Accessibility
- ListItem with Interactive Content
- Best Practices
- Enabling Screen Readers
.NET MAUI provides SemanticProperties which are attached properties that define information about which controls should receive accessibility focus and which text should be read aloud to the user. These properties set platform accessibility values so that a screen reader can speak about the element.
The SemanticProperties.Description attached property represents a short, descriptive text that a screen reader uses to announce an element. This property should be set for elements that have a meaning that's important for understanding the content or interacting with the user interface.
<Image Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!" />Or in C#:
Image image = new Image { Source = "dotnet_bot.png" };
SemanticProperties.SetDescription(image, "Cute dot net bot waving hi to you!");When you set SemanticProperties.Description on a parent element that has children, the behavior differs between platforms:
iOS (VoiceOver):
- All child elements are excluded from the accessibility tree
- Screen readers only announce the parent's custom description
- Users cannot navigate to any children, including interactive elements
Android (TalkBack):
- Non-interactive child elements (Label, Image, etc.) are excluded from the accessibility tree
- Interactive child elements (Button, Entry, Switch, etc.) remain accessible
- Users can still navigate to buttons and other interactive controls within the parent
This is native platform behavior and developers need to be aware of these differences!
<!-- Without Description: Each element is individually accessible -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="John Doe" />
<Label Text="Born: 1980-05-15" Grid.Row="1" />
<Button Text="Call" Grid.Row="2" Command="{Binding CallCommand}" />
</Grid>
<!-- Result iOS: 3 swipe gestures (2 labels + 1 button) -->
<!-- Result Android: 3 swipe gestures (2 labels + 1 button) -->
<!-- With Description: Platform differences appear -->
<Grid SemanticProperties.Description="John Doe, Born: 1980-05-15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="John Doe" />
<Label Text="Born: 1980-05-15" Grid.Row="1" />
<Button Text="Call" Grid.Row="2" Command="{Binding CallCommand}" />
</Grid>
<!-- Result iOS: 1 swipe gesture (parent description only, button excluded) -->
<!-- Result Android: 2 swipe gestures (parent description + button, labels excluded) -->When combining SemanticProperties.Description with dui:Touch.Command:
<Grid dui:Touch.Command="{Binding AddItemCommand}"
SemanticProperties.Description="Add new item card">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Text="Title" />
<Label Text="Subtitle" Grid.Row="1" />
<Button Text="Details" Grid.Row="2" Command="{Binding DetailsCommand}" />
</Grid>
<!-- Result iOS: 1 swipe gesture (parent as button, all children excluded) -->
<!-- Result Android: 2 swipe gestures (parent as button + Details button, labels excluded) -->Key insight: On iOS, setting Description on a parent excludes ALL children. On Android, it excludes non-interactive children (Labels, Images) but interactive elements (Buttons, Entries, Switches) remain accessible.
-
Avoid setting
Descriptionon aLabel. This will stop theTextproperty being spoken by the screen reader. The visual text should ideally match the text read aloud. -
Avoid setting
Descriptionon anEntryorEditoron Android. This will stop TalkBack actions from functioning. Instead, use thePlaceholderproperty or theHintattached property. - Be aware of platform differences: On iOS, children are completely excluded when you set Description on a parent. On Android, non-interactive children (Labels, Images) are excluded but interactive elements (Buttons, Entries) remain accessible.
- If you have interactive children on iOS and need them accessible, either don't set Description on the parent, or use a different approach for grouping information.
-
For consistent cross-platform behavior where you want to exclude all children on both platforms, use
AutomationProperties.ExcludedWithChildren="true"instead.
The SemanticProperties.Hint attached property provides additional context to the Description, such as the purpose of a control.
<Image Source="like.png"
SemanticProperties.Description="Like"
SemanticProperties.Hint="Like this post." />Or in C#:
Image image = new Image { Source = "like.png" };
SemanticProperties.SetDescription(image, "Like");
SemanticProperties.SetHint(image, "Like this post.");The SemanticProperties.HeadingLevel attached property enables an element to be marked as a heading to organize the UI and make it easier to navigate.
<Label Text="Main Heading"
SemanticProperties.HeadingLevel="Level1" />Or in C#:
Label label = new Label { Text = "Main Heading" };
SemanticProperties.SetHeadingLevel(label, SemanticHeadingLevel.Level1);Available heading levels: None, Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8, Level9
Note: Not all platforms support all heading levels. Check the specific platform documentation for details.
In addition to SemanticProperties, MAUI provides AutomationProperties for more advanced accessibility control. These are attached properties that can be added to any element to indicate how the element is reported to the underlying platform's accessibility framework.
The AutomationProperties.ExcludedWithChildren property determines if an element and its children should be excluded from the accessibility tree.
<StackLayout AutomationProperties.ExcludedWithChildren="true">
<!-- Children will not be visible to screen readers -->
</StackLayout>The AutomationProperties.IsInAccessibleTree property indicates whether the element is available in the accessibility tree. Must be set to true to use other automation properties.
<Entry AutomationProperties.IsInAccessibleTree="true" />Warning: On iOS, if IsInAccessibleTree is true on any control that has children, the screen reader will be unable to reach the children.
DIPS.Mobile.UI provides helpful attached properties that simplify common accessibility scenarios for screen readers. These helpers make it easier to implement accessibility best practices without writing platform-specific code.
The Accessibility.Mode attached property with the GroupChildren value simplifies grouping related information for screen readers. This implementation uses MAUI's SemanticProperties.Description under the hood to automatically group child elements - leveraging the native behavior where setting a description excludes children from the accessibility tree.
Platform behavior: On iOS, all children are excluded. On Android, non-interactive children (Labels) are excluded but interactive elements (Buttons, Entries) remain accessible. See Description behavior for details.
- Multiple child labels or text elements that form a single logical piece of information (e.g., address blocks, contact information, patient cards, product details)
- Read-only informational displays where all content should be announced together
- Card-like UI patterns where related information should be grouped
- When you want to reduce the number of swipe gestures needed for screen reader users
- When any child element is interactive (Button, Entry, Switch, etc.) - interactive elements need individual focus for usability
- When child elements represent separate, unrelated pieces of information that users might want to navigate individually
- When children have complex accessibility requirements or need custom hints/traits
- In lists or collections where each item should be individually navigable
<dui:VerticalStackLayout Spacing="{dui:Sizes size_1}"
Padding="{dui:Sizes size_3}"
BackgroundColor="{dui:Colors color_surface_subtle}"
dui:Accessibility.Mode="GroupChildren">
<dui:Label Text="John Doe"
Style="{dui:Styles Label=Body400}" />
<dui:Label Text="Born: 1980-05-15"
Style="{dui:Styles Label=UI200}" />
<dui:HorizontalStackLayout Spacing="{dui:Sizes size_1}">
<dui:Label Text="Phone:"
Style="{dui:Styles Label=UI200}" />
<dui:Label Text="+47 123 45 678"
Style="{dui:Styles Label=UI200}" />
</dui:HorizontalStackLayout>
<dui:Label Text="[email protected]"
Style="{dui:Styles Label=UI200}" />
</dui:VerticalStackLayout>Result: VoiceOver/TalkBack will read: "John Doe, Born: 1980-05-15, Phone:, +47 123 45 678, [email protected]"
Without this mode, screen readers would require 5 separate swipe gestures to read all information. With GroupChildren, it's read in one focus.
- Automatically collects text from all descendant
Labelelements - Collects any existing
SemanticProperties.Descriptionvalues from descendants - Combines all text with commas:
"Text1, Text2, Text3" - Sets the combined text as
SemanticProperties.Descriptionon the parent - Because
Descriptionis set, non-interactive children are excluded by the native platform behavior (all children on iOS, non-interactive children on Android)
- This mode does not track runtime changes - it collects text once when the layout is rendered
- If text in child labels changes after rendering, the accessibility description won't update
- If you add/remove children dynamically, the description won't reflect those changes
- The implementation relies on the native platform behavior where
SemanticProperties.Descriptionexcludes children (platform-specific: all children on iOS, non-interactive children on Android)
The Accessibility.Trait attached property allows you to set accessibility traits on any MAUI view, informing screen readers about the element's interactive nature and state. This is particularly useful for custom controls or when making non-standard elements interactive.
The Trait enum supports the [Flags] attribute, allowing multiple traits to be combined:
-
Trait.Button: Announces the element as a button, indicating it's tappable/interactive -
Trait.Selected: Announces the element as selected (e.g., in a radio button group) -
Trait.NotSelected: Announces the element as not selected
-
iOS: Maps to native
UIAccessibilityTraitflags-
Trait.Button→UIAccessibilityTrait.Button -
Trait.Selected→UIAccessibilityTrait.Selected
-
-
Android: Uses a custom
AccessibilityDelegatewithAccessibilityNodeInfo-
Trait.Button→ SetsClassName = "android.widget.Button" -
Trait.Selected→ SetsChecked = trueandCheckable = true -
Trait.NotSelected→ SetsChecked = falseandCheckable = true
-
Use Accessibility.Trait when:
- Creating custom interactive controls that aren't using the
Toucheffect - Building selection UI (like radio button groups, toggle cards, or selectable items)
- You need to explicitly mark an element as interactive without using
Touch.Command - You want to announce selection state changes to screen reader users
Note: When using dui:Touch.Command with SemanticProperties.Description, the Touch effect already adds the Button trait automatically. Use Accessibility.Trait="Button" only when you're not using the Touch effect.
Making a custom view interactive without using the Touch effect:
<!-- Without Touch effect - need to add Button trait manually -->
<Grid Gesture.TapGesture="{Binding SelectCommand}"
dui:Accessibility.Trait="Button"
SemanticProperties.Description="Select patient">
<Label Text="Patient Card" />
</Grid>
<!-- With Touch effect - Button trait is automatic, don't need to set it -->
<Grid dui:Touch.Command="{Binding SelectCommand}"
SemanticProperties.Description="Select patient">
<Label Text="Patient Card" />
</Grid>Result: Both announce as "Select patient, Button" to screen readers.
Indicating selection state in a radio button group or selectable list:
<!-- Selected item - using Touch effect -->
<Grid dui:Touch.Command="{Binding SelectOptionACommand}"
dui:Accessibility.Trait="Selected"
SemanticProperties.Description="Option A">
<Label Text="Option A" />
<Image Source="checkmark.png" IsVisible="True" />
</Grid>
<!-- Not selected item - using Touch effect -->
<Grid dui:Touch.Command="{Binding SelectOptionBCommand}"
dui:Accessibility.Trait="NotSelected"
SemanticProperties.Description="Option B">
<Label Text="Option B" />
<Image Source="checkmark.png" IsVisible="False" />
</Grid>Note: Since we're using dui:Touch.Command with SemanticProperties.Description, the Button trait is automatically added by the Touch effect. We only need to set the selection state (Selected or NotSelected).
Result:
- VoiceOver: "Option A, Selected, Button" and "Option B, Not Selected, Button"
- TalkBack: "Option A, Checked, Button" and "Option B, Not checked, Button"
Traits can be bound dynamically based on view model state:
<!-- Using Touch effect - Button trait is automatic -->
<Grid dui:Touch.Command="{Binding ToggleSelectionCommand}"
SemanticProperties.Description="{Binding OptionName}">
<Grid.Triggers>
<DataTrigger TargetType="Grid"
Binding="{Binding IsSelected}"
Value="True">
<Setter Property="dui:Accessibility.Trait" Value="Selected" />
</DataTrigger>
<DataTrigger TargetType="Grid"
Binding="{Binding IsSelected}"
Value="False">
<Setter Property="dui:Accessibility.Trait" Value="NotSelected" />
</DataTrigger>
</Grid.Triggers>
<Label Text="{Binding OptionName}" />
</Grid>Or in C#:
// Without Touch effect - need to add Button trait
Accessibility.SetTrait(myView, Trait.Button);
// Combine multiple traits (without Touch effect)
Accessibility.SetTrait(myView, Trait.Button | Trait.Selected);
// With Touch effect - only set selection state
// (Button trait is already added by Touch effect)
if (isSelected)
Accessibility.SetTrait(myView, Trait.Selected);
else
Accessibility.SetTrait(myView, Trait.NotSelected);Accessibility.Trait works seamlessly with other accessibility properties:
<!-- Using Touch effect - Button trait is automatic, only set selection state -->
<Grid dui:Touch.Command="{Binding SelectCommand}"
dui:Accessibility.Trait="Selected"
SemanticProperties.Description="Dark mode theme"
SemanticProperties.Hint="Double tap to select this theme">
<Label Text="Dark Mode" />
<Image Source="checkmark.png" />
</Grid>Result: Screen readers provide full context: description, trait, and hint together.
-
Don't add Button trait when using Touch effect: The
dui:Touch.CommandwithSemanticProperties.Descriptionalready adds Button trait automatically. Only add Button trait manually when not using Touch:<!-- ✅ Good: Using Touch effect, only set selection state --> <Grid dui:Touch.Command="{Binding Command}" dui:Accessibility.Trait="Selected" SemanticProperties.Description="Option A" /> <!-- ✅ Good: Not using Touch, add Button trait manually --> <Grid dui:Accessibility.Trait="Button, Selected" SemanticProperties.Description="Option A" /> <!-- ❌ Redundant: Button trait added twice --> <Grid dui:Touch.Command="{Binding Command}" dui:Accessibility.Trait="Button, Selected" SemanticProperties.Description="Option A" />
-
Toggle between Selected and NotSelected: Don't use both at once - they're mutually exclusive states:
<!-- ✅ Good: One or the other --> dui:Accessibility.Trait="Selected" dui:Accessibility.Trait="NotSelected" <!-- ❌ Bad: Both together --> dui:Accessibility.Trait="Selected, NotSelected"
-
Always provide Description with Trait: Traits describe what an element is, but
SemanticProperties.Descriptiondescribes what it does or what it represents:<Grid dui:Accessibility.Trait="Selected" SemanticProperties.Description="Large font size">
-
Use NotSelected explicitly: Even though "not selected" is the default, explicitly setting
NotSelectedhelps screen readers announce the state clearly to users. -
Test with actual screen readers: Trait announcements vary slightly between VoiceOver and TalkBack - always test on both platforms.
The Accessibility.Trait property is implemented using the MAUI PlatformEffect pattern:
- Creates a platform effect that applies native accessibility traits
- Automatically updates when the trait value changes
- Works with any MAUI
Viewelement - Properly handles multiple traits through the
[Flags]enum
When using the Touch effect (from Touch) to make elements interactive, proper accessibility support is critical. The Touch effect automatically enhances accessibility when SemanticProperties.Description is set.
When you set SemanticProperties.Description on an element with Touch.Command, the Touch effect automatically configures the native platform accessibility traits:
-
iOS: Appends
UIAccessibilityTrait.Buttonto the element's accessibility traits -
Android: Sets the accessibility class name to
"android.widget.Button"
This ensures screen readers (VoiceOver/TalkBack) announce the element as a "Button", providing clear feedback about its interactive nature.
Note: If you do not want this behavior, it can be disabled by setting the dui:Touch.IsButtonTraitEnabled property to false.
<Grid dui:Touch.Command="{Binding SelectPatientCommand}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<VerticalStackLayout>
<Label Text="John Doe" />
<Label Text="Born: 1980-05-15" />
</VerticalStackLayout>
<Label Grid.Column="1" Text="→" />
</Grid>Problem: Screen reader users won't know this element is tappable. They'll hear each label separately without any indication it's an interactive button.
<Grid dui:Touch.Command="{Binding SelectPatientCommand}"
SemanticProperties.Description="Select patient John Doe, Born 1980-05-15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<VerticalStackLayout>
<Label Text="John Doe" />
<Label Text="Born: 1980-05-15" />
</VerticalStackLayout>
<Label Grid.Column="1" Text="→" />
</Grid>Result: Screen readers will announce: "Select patient John Doe, Born 1980-05-15, Button" - users know it's interactive!
Note: Remember that setting SemanticProperties.Description on the Grid excludes the child Labels from accessibility (see Description behavior).
If you want to automatically combine the text from children without manually writing the description:
<Grid dui:Touch.Command="{Binding SelectPatientCommand}"
dui:Accessibility.Mode="GroupChildren">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<VerticalStackLayout>
<Label Text="John Doe" />
<Label Text="Born: 1980-05-15" />
</VerticalStackLayout>
<Label Grid.Column="1" Text="→" />
</Grid>Result: The GroupChildren mode automatically collects text from all Labels and sets SemanticProperties.Description to "John Doe, Born: 1980-05-15, →". The Touch effect then adds the Button trait, so screen readers announce: "John Doe, Born: 1980-05-15, →, Button"
This approach is convenient when you have dynamic content or don't want to manually maintain the description text. See GroupChildren Mode for more details.
⚠️ Always setSemanticProperties.Descriptionwhen usingTouch.Commandon non-button elements- The Touch effect monitors for changes to
SemanticProperties.Descriptionand updates accessibility traits automatically - Remember that setting
Descriptionon a parent has platform-specific behavior: on iOS it excludes all children, on Android it excludes non-interactive children but interactive elements remain accessible (see Description behavior)
When a ListItem contains interactive controls (like Switch, Button, Entry, or other tappable elements), screen readers on iOS (VoiceOver) and Android (TalkBack) will focus on the ListItem's title and subtitle first before reaching the interactive element. This creates unnecessary navigation steps and a poor accessibility experience for users with screen readers.
Consider this example with a switch:
<dui:ListItem Title="Enable notifications"
Subtitle="Receive alerts when new messages arrive">
<dui:Switch IsToggled="{Binding NotificationsEnabled}" />
</dui:ListItem>Screen reader behavior:
- First focus: "Enable notifications"
- Second focus: "Receive alerts when new messages arrive"
- Third focus: "Switch" (the actual interactive element)
Screen reader users must perform 3 separate swipe gestures to reach the switch, hearing redundant information along the way.
The DisableInternalAccessibility property excludes the ListItem's internal elements (title, subtitle, and icon) from the accessibility tree, allowing screen readers to focus directly on the interactive control.
<dui:ListItem Title="Enable notifications"
Subtitle="Receive alerts when new messages arrive"
DisableInternalAccessibility="True">
<dui:Switch IsToggled="{Binding NotificationsEnabled}"
SemanticProperties.Description="Enable notifications. Receive alerts when new messages arrive" />
</dui:ListItem>Screen reader behavior:
- Only focus: "Enable notifications. Receive alerts when new messages arrive, Switch"
Now screen reader users reach the interactive control in just 1 swipe gesture with full context!
When DisableInternalAccessibility="True":
- The title and subtitle container is excluded from the accessibility tree using
AutomationProperties.ExcludedWithChildren="true" - The icon (if present) is also excluded from the accessibility tree
- Screen readers skip directly to any interactive content inside the ListItem
- Works consistently on both iOS (VoiceOver) and Android (TalkBack)
When using DisableInternalAccessibility="True", always set SemanticProperties.Description on the interactive element with descriptive text that includes the context from the title/subtitle. This ensures screen reader users get the complete information.
<!-- ✅ Good: Full context provided -->
<dui:ListItem Title="Dark mode"
Subtitle="Use dark theme throughout the app"
DisableInternalAccessibility="True">
<dui:Switch SemanticProperties.Description="Dark mode. Use dark theme throughout the app"
IsToggled="{Binding DarkModeEnabled}" />
</dui:ListItem>
<!-- ❌ Bad: Missing context -->
<dui:ListItem Title="Dark mode"
Subtitle="Use dark theme throughout the app"
DisableInternalAccessibility="True">
<dui:Switch IsToggled="{Binding DarkModeEnabled}" />
<!-- Screen reader will just say "Switch" without any context! -->
</dui:ListItem>Use DisableInternalAccessibility="True" when:
- The ListItem contains a single interactive control (Switch, Button, Entry, etc.)
- The title and subtitle are purely descriptive labels for that control
- You want to reduce navigation steps for screen reader users
- The interactive element can meaningfully incorporate the title/subtitle context in its description
Don't use DisableInternalAccessibility="True" when:
- The ListItem contains multiple interactive elements (users need to navigate between them)
- The title/subtitle contain information separate from the interactive control's purpose
- The ListItem is purely informational without interactive content
- You're using the ListItem's built-in tap functionality (via
Commandproperty)
<dui:ListItem Title="Show archived items"
Subtitle="Include archived items in the list"
DisableInternalAccessibility="True">
<dui:Switch SemanticProperties.Description="Show archived items. Include archived items in the list"
IsToggled="{Binding ShowArchivedItems}" />
</dui:ListItem><dui:ListItem Title="Export data"
Subtitle="Download your data as a CSV file"
DisableInternalAccessibility="True">
<dui:Button Text="Export"
Command="{Binding ExportCommand}"
SemanticProperties.Description="Export data. Download your data as a CSV file" />
</dui:ListItem><dui:ListItem Title="Email address"
Subtitle="Your contact email"
DisableInternalAccessibility="True">
<dui:Entry Placeholder="[email protected]"
Text="{Binding Email}"
SemanticProperties.Description="Email address. Your contact email"
Keyboard="Email" />
</dui:ListItem><!-- ❌ Bad: Don't disable internal accessibility with multiple interactive elements -->
<dui:ListItem Title="Notification settings"
Subtitle="Configure your notification preferences">
<!-- Users need to access both buttons independently -->
<HorizontalStackLayout Spacing="8">
<dui:Button Text="Email" Command="{Binding ConfigureEmailCommand}" />
<dui:Button Text="Push" Command="{Binding ConfigurePushCommand}" />
</HorizontalStackLayout>
</dui:ListItem>-
Make your UI self-describing: Test that all elements are screen reader accessible. Add descriptive text and hints when necessary.
-
Provide alternate text for images and icons: Always use
SemanticProperties.Descriptionfor meaningful images. -
Group related information: Use
Accessibility.Mode="GroupChildren"for card-like patterns with multiple labels representing a single piece of information. Note: On iOS all children are excluded, on Android interactive children remain accessible. -
Exclude decorative elements: Use
AutomationProperties.ExcludedWithChildren="true"for purely decorative elements, or set a customSemanticProperties.Descriptionon the parent (which has platform-specific child exclusion behavior). -
Don't mix visual and semantic text: If you set a custom
SemanticProperties.Description, ensure it matches the visual content users can see. -
Test with actual screen readers: Enable VoiceOver (iOS) or TalkBack (Android) and navigate through your app to ensure a smooth experience.
-
Support large fonts and high contrast: Use dynamic layouts that can accommodate larger text sizes.
-
Localize accessibility descriptions: When your app supports multiple languages, ensure all accessibility text is also localized.
-
Keep interactive elements separate on iOS: Don't use
GroupChildrenor setDescriptionon parents containing interactive controls if you need them accessible on iOS. On Android, interactive elements remain accessible even with parent descriptions. -
Follow WCAG guidelines: Ensure your app is perceivable, operable, understandable, and robust for all users. See Web Content Accessibility Guidelines (WCAG).
-
Choose the right approach:
- For custom descriptions: Use
SemanticProperties.Descriptiondirectly (remember platform differences in child exclusion!) - For automatic grouping: Use DIPS.Mobile.UI's
Accessibility.Mode="GroupChildren"which automatically collects and combines text - For excluding decorative elements: Use
AutomationProperties.ExcludedWithChildren="true"
- For custom descriptions: Use
-
Understand the Description behavior: Always remember that
SemanticProperties.Descriptionhas platform-specific behavior regarding children. On iOS, all children are excluded. On Android, non-interactive children (Labels, Images) are excluded but interactive elements (Buttons, Entries, Switches) remain accessible. See Description behavior for details. -
Always set Description with Touch.Command: When using the
Toucheffect to make elements interactive, always setSemanticProperties.Descriptionso screen readers announce the element as a "Button". See details in Touch Effect Accessibility. -
Use DisableInternalAccessibility for ListItems with interactive content: When a ListItem contains a single interactive control (Switch, Button, Entry, etc.), use
DisableInternalAccessibility="True"to skip the title/subtitle and focus directly on the control. Always setSemanticProperties.Descriptionon the interactive element with the full context. See details in ListItem with Interactive Content. -
Use Accessibility.Trait for custom interactive controls and selection states: When building custom controls or selection UI, use
Accessibility.Traitto announce button behavior and selection states to screen readers. Always combine withSemanticProperties.Descriptionfor complete context. See details in Accessibility.Trait.
- Open the Settings app
- Select Accessibility > VoiceOver
- Turn VoiceOver on
For more information: Apple VoiceOver Guide
- Open the Settings app
- Select Accessibility > TalkBack
- Turn Use TalkBack on
For more information: Google TalkBack Guide