Building Accessible Apps with Compose: A Complete Guide

By Engineering Team • Published: 2025-12-13 • Updated: 2025-12-1314 min read

AccessibilitySemanticsTalkBackInclusive Design

Why Accessibility Matters

Over 1 billion people worldwide have disabilities. Building accessible apps is not just ethical — it is often legally required and expands your user base. Compose makes accessibility easier than ever with its semantic tree approach.

The semantic tree is a parallel structure to the UI tree that accessibility services like TalkBack use to understand your app. Every composable can contribute to this tree.

Content Descriptions for Images

Screen readers cannot see images. Always provide meaningful descriptions:

// Informative image - describe what it shows
Image(
    painter = painterResource(R.drawable.product_photo),
    contentDescription = "Red running shoes, side view"
)

// Decorative image - hide from accessibility tree
Image(
    painter = painterResource(R.drawable.decorative_divider),
    contentDescription = null  // Explicitly null for decorative
)

// Icon button - describe the action, not the icon
IconButton(onClick = { /* delete */ }) {
    Icon(
        Icons.Default.Delete,
        contentDescription = "Delete item"  // Not "trash can icon"
    )
}

Semantic Merging and Grouping

By default, every Text and clickable element is a separate node. Merge related content for better screen reader experience:

// Before: TalkBack reads "John Doe" then "Senior Engineer" separately
Row {
    Text("John Doe")
    Text("Senior Engineer")
}

// After: TalkBack reads "John Doe, Senior Engineer" as one item
Row(modifier = Modifier.semantics(mergeDescendants = true) {}) {
    Text("John Doe")
    Text("Senior Engineer")
}

// For interactive items with multiple text elements
Card(
    modifier = Modifier
        .clickable { openProfile() }
        .semantics(mergeDescendants = true) {}
) {
    Text("John Doe")
    Text("Senior Engineer")
    Text("View Profile")
}

Touch Target Size

Small touch targets are hard for users with motor impairments. Material 3 enforces minimum 48dp targets automatically, but custom components need manual handling:

// Ensure minimum touch target regardless of visual size
Box(
    modifier = Modifier
        .size(24.dp)  // Visual size
        .sizeIn(minWidth = 48.dp, minHeight = 48.dp)  // Touch target
        .clickable { /* action */ },
    contentAlignment = Alignment.Center
) {
    Icon(Icons.Default.Add, contentDescription = "Add item")
}

Testing Accessibility

Always test with real accessibility tools:

  • Enable TalkBack and navigate your app using only gestures
  • Run Accessibility Scanner from Play Store to find issues
  • Use Layout Inspector to view the semantic tree
  • Write accessibility tests with composeTestRule.onNode(hasContentDescription(...))
Note: Accessibility testing should be part of your regular QA process, not an afterthought. Include it in your PR checklist.
← Back to Blog