DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Compose Accessibility Guide — Building a11y-Ready UI

Compose Accessibility Guide — Building a11y-Ready UI

Accessibility isn't optional. Jetpack Compose provides powerful tools to build inclusive interfaces.

contentDescription for Images

Image(
    painter = painterResource(R.drawable.ic_profile),
    contentDescription = "User profile picture"
)

Icon(
    imageVector = Icons.Filled.Check,
    contentDescription = "Task completed"
)
Enter fullscreen mode Exit fullscreen mode

Rule: Every non-decorative image needs a description.

Semantics & Heading Role

Text(
    text = "Settings",
    modifier = Modifier.semantics {
        heading()
    }
)

Button(
    onClick = { },
    modifier = Modifier.semantics {
        role = Role.Button
        stateDescription = "Enabled"
    }
) {
    Text("Submit")
}
Enter fullscreen mode Exit fullscreen mode

mergeDescendants for Card Readout

Card(
    modifier = Modifier.semantics(mergeDescendants = true) {
        contentDescription = "User card: Alice, 5 followers"
    }
) {
    Column {
        Text("Alice")
        Text("5 followers")
    }
}
Enter fullscreen mode Exit fullscreen mode

Screen readers read the merged description instead of each child.

CustomAccessibilityAction for Alternatives

Button(
    modifier = Modifier.semantics {
        customActions = listOf(
            CustomAccessibilityAction("Swipe left to delete") { true }
        )
    },
    onClick = { }
) {
    Text("Delete")
}
Enter fullscreen mode Exit fullscreen mode

Provides keyboard alternative for gesture-based interactions.

Live Region for Dynamic Updates

Text(
    text = "Loading: $progress%",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Assertive
    }
)
Enter fullscreen mode Exit fullscreen mode

Announces changes to screen readers.

Minimum Touch Target

Button(
    modifier = Modifier.size(48.dp),
    onClick = { }
) {
    Text("Tap")
}
Enter fullscreen mode Exit fullscreen mode

48dp minimum for touch targets (WCAG 2.1).

Form Accessibility

TextField(
    value = email,
    onValueChange = { email = it },
    label = { Text("Email") },
    modifier = Modifier.semantics {
        contentDescription = "Email address required"
        error = if (email.isEmpty()) "Email cannot be empty" else null
    }
)
Enter fullscreen mode Exit fullscreen mode

Include error semantics for form validation.

Checklist

  • ✓ Every image has contentDescription
  • ✓ Headings marked with semantics
  • ✓ Cards use mergeDescendants
  • ✓ Minimum 48dp touch targets
  • ✓ Dynamic updates use liveRegion
  • ✓ Forms include error semantics
  • ✓ Test with TalkBack (Android) or VoiceOver (iOS)

Accessibility benefits everyone: better screen reader support, clearer semantics, improved usability. Build inclusively from the start.


Interested in mobile app development? Check out 8 Android app templates on Gumroad!

Top comments (0)