Aside from the primary information that a composable carries, like a text string of a[`Text`](https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#Text(kotlin.String,androidx.compose.ui.Modifier,androidx.compose.ui.graphics.Color,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.font.FontStyle,androidx.compose.ui.text.font.FontWeight,androidx.compose.ui.text.font.FontFamily,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.style.TextDecoration,androidx.compose.ui.text.style.TextAlign,androidx.compose.ui.unit.TextUnit,androidx.compose.ui.text.style.TextOverflow,kotlin.Boolean,kotlin.Int,kotlin.Int,kotlin.Function1,androidx.compose.ui.text.TextStyle))composable, it can be helpful to have more supplemental information about UI elements.

Information about the meaning and role of a component in Compose is called**semantics**, which are a way to provide additional context about composables to services like accessibility, autofill, and testing. For example, a camera icon might visually just be an image, but the semantic meaning could be "Take a photo".

By combining the appropriate semantics with the appropriate Compose APIs, you provide as much information about your component as possible to accessibility services, which then decide how to represent it to the user.

Material and Compose UI and Foundation APIs come with built-in semantics that follow their specific role and function, but you can also modify these semantics for existing APIs or set new ones for custom components, according to your specific requirements.

## Semantic properties

Semantic properties convey the meaning of the corresponding composable. For example, the`Text`composable contains a semantic property`text`, because that's the*meaning* of that composable. An`Icon`contains a`contentDescription`property (if set by the developer) that conveys in text what the meaning of the icon is.

Consider how semantics properties convey the meaning of a composable. Consider a`Switch`. This is how it looks to the user:
![](https://developer.android.com/static/develop/ui/compose/images/semantics-switches.png)**Figure 1.** A`Switch`in its "On" and "Off" state.

To describe the*meaning* of this element, you could say the following:*"This is a Switch, which is a toggleable element in its 'On' state. You can click it to interact with it."*

This is exactly what the semantics properties are used for. The semantics node of this Switch element contains the following properties, as visualized with the Layout Inspector:
![Layout Inspector showing the Semantics properties of a Switch composable](https://developer.android.com/static/develop/ui/compose/images/semantics-layout-inspector-switch.png)**Figure 2.** Layout Inspector showing the Semantics properties of a`Switch`composable.

The`Role`indicates the type of element. The`StateDescription`describes how the "On" state should be referenced. By default this is a localized version of the word "On", but this can be made more specific (for example, "Enabled") based on the context. The`ToggleableState`is the current state of the Switch. The`OnClick`property references the method used to interact with this element.

Keeping track of the semantics properties of each composable in your app unlocks a lot of powerful possibilities:

- Accessibility services use the properties to represent UI shown on the screen and allow users to interact with it. For the Switch composable, Talkback might announce: "On; Switch; double tap to toggle". The user can double tap their screen to toggle the Switch off.
- The Testing framework uses the properties to find nodes, interact with them, and make assertions:  

  ```kotlin
  val mySwitch = SemanticsMatcher.expectValue(
      SemanticsProperties.Role, Role.Switch
  )
  composeTestRule.onNode(mySwitch)
      .performClick()
      .assertIsOff()https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/androidTest/java/com/example/compose/snippets/semantics/SemanticsSnippets.kt#L61-L66
  ```

Composables and modifiers that are built on top of the Compose[foundation library](https://developer.android.com/jetpack/androidx/releases/compose-foundation)already set the relevant properties for you by default. Optionally, you can change these properties manually, to improve the accessibility support for specific use cases, or change your composables'[merging or clearing strategy](https://developer.android.com/develop/ui/compose/accessibility/merging-clearing).

To signal the specific content type of your component to accessibility services, you can apply a variety of different semantics. These additions will support the main semantic information in place and help accessibility services fine-tune how your component is represented, announced, or interacted with.

For a full list of semantics properties, see the[`SemanticsProperties`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/SemanticsProperties)object. For a full list of possible Accessibility Actions, see the[`SemanticsActions`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/SemanticsActions)object.

### Headings

Apps often contain screens with text-rich content, like long articles or news pages, which are usually divided into different subsections with headings:
![A blog post with article text in a scrollable container.](https://developer.android.com/static/develop/ui/compose/images/a11y-blog.png)**Figure 3.**A blog post with the article text in a scrollable container.

Users with accessibility needs can have difficulties navigating such a screen easily. To improve the navigation experience, some accessibility services allow for easier navigation directly between sections or headings. To enable this, indicate that your component is a[`heading`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).heading())by defining its semantics property:

<br />

```kotlin
@Composable
private fun Subsection(text: String) {
    Text(
        text = text,
        style = MaterialTheme.typography.headlineSmall,
        modifier = Modifier.semantics { heading() }
    )
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L312-L319
```

<br />

### Alerts and pop ups

If your component is an alert or a pop up, like a`Snackbar`, you might want to signal to accessibility services that a new structure or updates to content can be conveyed to users.

Alert-like components can be marked with the[`liveRegion`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).liveRegion())semantics property. This allows accessibility services to automatically notify the user of changes to this component, or its children:

<br />

```kotlin
PopupAlert(
    message = "You have a new message",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Polite
    }
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L522-L527
```

<br />

You should use`liveRegionMode.Polite`in most cases where users' attention should only briefly be drawn to alerts or important changing content on-screen.

You should use`liveRegion.Assertive`sparingly to avoid disruptive feedback. It should be used for situations where it's crucial that users are made aware of time-sensitive content:

<br />

```kotlin
PopupAlert(
    message = "Emergency alert incoming",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Assertive
    }
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L531-L536
```

<br />

Live regions shouldn't be used on content that updates frequently, such as countdown timers, to avoid overwhelming users with constant feedback.

### Window-like components

Window-like custom components, similar to`ModalBottomSheet`, need additional signals to differentiate them from surrounding content. For this, you can use[`paneTitle`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).paneTitle())semantics, so that any relevant window or pane changes may be represented appropriately by the accessibility services, along with its main semantic information:

<br />

```kotlin
ShareSheet(
    message = "Choose how to share this photo",
    modifier = Modifier
        .fillMaxWidth()
        .align(Alignment.TopCenter)
        .semantics { paneTitle = "New bottom sheet" }
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L541-L547
```

<br />

For reference, see[how Material 3 uses`paneTitle`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ModalBottomSheet.kt;drc=ff49291d65d63dc436bb0aa4c8effa9c54d1d005;l=283)for its components.

### Error components

For other content types, such as error-like components, you might want to expand on the main semantic information for users with accessibility needs. When defining error states, you can inform the accessibility services of its[`error`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).error(kotlin.String))semantics, and provide expanded error messaging.

In this example, TalkBack reads the main error text information, followed by additional, expanded messaging:

<br />

```kotlin
Error(
    errorText = "Fields cannot be empty",
    modifier = Modifier
        .semantics {
            error("Please add both email and password")
        }
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L552-L558
```

<br />

### Progress tracking components

For custom components that track progress, you might want to notify users of their progress changes, including the current progress value, its range, and step size. You can do so with[`progressBarRangeInfo`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).progressBarRangeInfo())semantics―this ensures that accessibility services are aware of progress changes, and can update users accordingly. Different assistive technologies may also have unique ways of hinting at increasing and decreasing progression.

<br />

```kotlin
ProgressInfoBar(
    modifier = Modifier
        .semantics {
            progressBarRangeInfo =
                ProgressBarRangeInfo(
                    current = progress,
                    range = 0F..1F
                )
        }
)https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L563-L572
```

<br />

### List and item information

In custom lists and grids with many items, it might be helpful for accessibility services to also receive more detailed information, like the total number of items and indices.

By using[`collectionInfo`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).collectionInfo())and[`collectionItemInfo`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).collectionItemInfo())semantics on the list and items respectively, in this long list, accessibility services can inform users of what item index they are at out of the total collection, in addition to textual semantic information:

<br />

```kotlin
MilkyWayList(
    modifier = Modifier
        .semantics {
            collectionInfo = CollectionInfo(
                rowCount = milkyWay.count(),
                columnCount = 1
            )
        }
) {
    milkyWay.forEachIndexed { index, text ->
        Text(
            text = text,
            modifier = Modifier.semantics {
                collectionItemInfo =
                    CollectionItemInfo(index, 0, 0, 0)
            }
        )
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L577-L595
```

<br />

### State description

A composable can define a[`stateDescription`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).stateDescription())for semantics which the Android framework uses to read out the state that the composable is in. For example, a toggleable composable can be in either a "checked" or an "unchecked" state. In some cases, you might want to override the default state description labels that Compose uses. You can do so by explicitly specifying the state description labels before defining a composable as toggleable:

<br />

```kotlin
@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
    val stateSubscribed = stringResource(R.string.subscribed)
    val stateNotSubscribed = stringResource(R.string.not_subscribed)
    Row(
        modifier = Modifier
            .semantics {
                // Set any explicit semantic properties
                stateDescription = if (selected) stateSubscribed else stateNotSubscribed
            }
            .toggleable(
                value = selected,
                onValueChange = { onToggle() }
            )
    ) {
        /* ... */
    }
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L291-L308
```

<br />

### Custom actions

[Custom actions](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.semantics.SemanticsPropertyReceiver).customActions())can be used for more complex touchscreen gestures, like swipe to dismiss or drag and drop, as these can be challenging for users with motor impairments or other disabilities to interact with.

To make the[swipe to dismiss gesture](https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#SwipeToDismissBox(androidx.compose.material3.SwipeToDismissBoxState,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Boolean,kotlin.Boolean,kotlin.Boolean,kotlin.Function1))more accessible, you can link it to a custom action, passing the dismissal action and label there:

<br />

```kotlin
SwipeToDismissBox(
    modifier = Modifier.semantics {
        // Represents the swipe to dismiss for accessibility
        customActions = listOf(
            CustomAccessibilityAction(
                label = "Remove article from list",
                action = {
                    removeArticle()
                    true
                }
            )
        )
    },
    state = rememberSwipeToDismissBoxState(),
    backgroundContent = {}
) {
    ArticleListItem()
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L599-L616
```

<br />

An accessibility service like TalkBack then highlights the component, and hints that there are more actions available in its menu, representing the swipe to dismiss action there:
![Visualization of TalkBack action menu](https://developer.android.com/static/develop/ui/compose/images/talkback_menu_actions_expanded.png)**Figure 4.**Visualization of TalkBack action menu.

Another use case for custom actions are long lists with items that have more available actions, as it might be tedious for users to iterate through each action for every single item individually:
![=Visualization of Switch Access navigation on screen](https://developer.android.com/static/develop/ui/compose/images/switch_access_article_selection.png)**Figure 5.**Visualization of Switch Access navigation on screen.

To improve the navigation experience, which is especially helpful for interaction-based assistive technologies like Switch Access or Voice Access, you can use custom actions on the container to move actions out of individual traversal, and into a separate action menu:

<br />

```kotlin
ArticleListItemRow(
    modifier = Modifier
        .semantics {
            customActions = listOf(
                CustomAccessibilityAction(
                    label = "Open article",
                    action = {
                        openArticle()
                        true
                    }
                ),
                CustomAccessibilityAction(
                    label = "Add to bookmarks",
                    action = {
                        addToBookmarks()
                        true
                    }
                ),
            )
        }
) {
    Article(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = openArticle,
    )
    BookmarkButton(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = addToBookmarks,
    )
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/accessibility/AccessibilitySnippets.kt#L620-L649
```

<br />

In these cases, make sure to manually clear the original children's semantics with the[`clearAndSetSemantics`](https://developer.android.com/reference/kotlin/androidx/compose/ui/semantics/package-summary#(androidx.compose.ui.Modifier).clearAndSetSemantics(kotlin.Function1))modifier, as you are moving them into custom actions.

Using Switch Access as an example, its menu opens upon selection of the container and lists the available nested actions there:
![Switch Access highlight of the article list item](https://developer.android.com/static/develop/ui/compose/images/switch_access_entire_item_selection.png)**Figure 6.**Switch Access highlight of the article list item.![Visualization of Switch Access action menu.](https://developer.android.com/static/develop/ui/compose/images/switch_access_menu.png)**Figure 7.**Visualization of Switch Access action menu.

## Semantics tree

A composition[describes the UI](https://developer.android.com/develop/ui/compose/mental-model)of your app and is produced by running composables. The composition is a tree-structure that consists of the composables that describe your UI.

Next to the Composition, there exists a parallel tree, called the*semantics tree* . This tree describes your UI in an alternative manner that is understandable for[Accessibility](https://developer.android.com/develop/ui/compose/accessibility)services and for the[Testing](https://developer.android.com/develop/ui/compose/testing)framework. Accessibility services use the tree to describe the app to users with a specific need. The testing framework uses the tree to interact with your app and make assertions about it. The Semantics tree does not contain the information on how to*draw* your composables, but it contains information about the**semantic*meaning***of your composables.
![A typical UI hierarchy and its semantics tree](https://developer.android.com/static/develop/ui/compose/images/semantics-ui-tree.png)**Figure 8.**A typical UI hierarchy and its semantics tree.

If your app consists of composables and modifiers from the Compose foundation and material library, the Semantics tree is automatically filled and generated for you. However**when you're adding custom low-level composables, you have to manually provide its semantics**. There might also be situations where your tree does not correctly or fully represent the meaning of the elements on the screen, in which case you can adapt the tree.

Consider for example this custom calendar composable:
![A custom calendar composable with selectable day elements](https://developer.android.com/static/develop/ui/compose/images/semantics-calendar.png)**Figure 9.**A custom calendar composable with selectable day elements.

In this example, the entire calendar is implemented as a single low-level composable, using the`Layout`composable and drawing directly to the`Canvas`. If you don't do anything else, accessibility services won't receive enough information about the content of the composable and the user's selection within the calendar. For example, if a user clicks on the day containing 17, the accessibility framework only receives the description information for the whole calendar control. In this case, the TalkBack accessibility service would announce "Calendar" or, only slightly better, "April Calendar" and the user would be left to wonder what day was selected. To make this composable more accessible, you'll need to add semantic information manually.

### Merged and unmerged tree

As mentioned before, each composable in the UI tree might have zero or more semantics properties set. When a composable has no semantics properties set, it isn't included as part of the Semantics tree. That way, the Semantics tree contains only the nodes that actually contain semantic meaning. However, sometimes to convey the correct meaning of what is shown on the screen, it is also useful to merge certain sub-trees of nodes and treat them as one. That way you can reason about a set of nodes as a whole, instead of dealing with each descendant node individually. As a rule of thumb, each node in this tree represents a focusable element when using Accessibility services.

An example of such a composable is`Button`. You can reason about a button as a single element, even though it may contain multiple child nodes:

<br />

```kotlin
Button(onClick = { /*TODO*/ }) {
    Icon(
        imageVector = Icons.Filled.Favorite,
        contentDescription = null
    )
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Like")
}https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/main/java/com/example/compose/snippets/semantics/SemanticsSnippets.kt#L37-L44
```

<br />

In the Semantics tree, the properties of the button's descendants are merged, and the button is presented as a single leaf node in the tree:
![Merged single leaf semantics representation](https://developer.android.com/static/develop/ui/compose/images/semantics-merged-single-leaf.png)**Figure 10.**Merged single leaf semantics representation.

Composables and modifiers can indicate that they want to merge their descendants' semantics properties by calling`Modifier.semantics
(mergeDescendants = true) {}`. Setting this property to`true`indicates that the semantics properties should be merged. In the`Button`example, the`Button`composable uses the`clickable`modifier internally that includes this`semantics`modifier. Therefore, the descendant nodes of the button are merged. Read the accessibility documentation to learn more about when you should[change merging behavior](https://developer.android.com/develop/ui/compose/accessibility/merging-clearing)in your composable.

Several modifiers and composables in the Foundation and Material Compose libraries have this property set. For example, the`clickable`and`toggleable`modifiers will automatically merge their descendants. Also, the`ListItem`composable will merge its descendants.

### Inspect the tree

The semantics tree is in fact two different trees. There's a*merged* Semantics tree, which merges descendant nodes when`mergeDescendants`is set to`true`. There's also an*unmerged* Semantics tree, which does not apply the merging, but keeps every node intact. Accessibility services use the unmerged tree and apply their own merging algorithms, taking into consideration the`mergeDescendants`property. The testing framework uses the merged tree by default.

You can inspect both trees with the`printToLog()`method. By default, and as in the earlier examples, the merged tree is logged. To print the unmerged tree instead, set the`useUnmergedTree`parameter of the`onRoot()`matcher to`true`:

<br />

```kotlin
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/androidTest/java/com/example/compose/snippets/semantics/SemanticsSnippets.kt#L78-L78
```

<br />

The Layout Inspector lets you display both the merged and the unmerged Semantics tree, by selecting the preferred one in the view filter:
![Layout Inspector view options, allowing both the display of the merged and the unmerged Semantics tree](https://developer.android.com/static/develop/ui/compose/images/semantics-layout-inspector-menu.png)**Figure 11.**Layout Inspector view options, allowing both the display of the merged and the unmerged Semantics tree.

For each node in your tree, the Layout Inspector shows both the Merged Semantics and the Semantics set on that node in the properties panel:
![Semantics properties merged and set](https://developer.android.com/static/develop/ui/compose/images/semantics-merged-and-set.png)**Figure 12.**Semantics properties merged and set.

By default, matchers in the Testing Framework use the merged Semantics tree. That's why you can interact with a`Button`by matching the text shown inside it:

<br />

```kotlin
composeTestRule.onNodeWithText("Like").performClick()https://github.com/android/snippets/blob/95aeebd507b29719a9e7d5a839f101bbbe42ea72/compose/snippets/src/androidTest/java/com/example/compose/snippets/semantics/SemanticsSnippets.kt#L81-L81
```

<br />

Override this behavior by setting the`useUnmergedTree`parameter of the matchers to`true`, as with the`onRoot`matcher.

### Adapt the tree

As mentioned before, you can override or clear certain semantics properties or change the merging behavior of the tree. This is particularly relevant when you're creating your own custom components. Without setting the correct properties and merge behavior, your app might not be accessible, and tests might behave differently than you expect. If you want to learn more about testing, see the[testing guide](https://developer.android.com/develop/ui/compose/testing).

## Recommended for you

- Note: link text is displayed when JavaScript is off
- [Accessibility in Compose](https://developer.android.com/develop/ui/compose/accessibility)
- [Material Design 2 in Compose](https://developer.android.com/develop/ui/compose/designsystems/material)
- [Testing your Compose layout](https://developer.android.com/develop/ui/compose/testing)