From cc-mobile-android
A11y patterns for this Android project — semantics modifiers, content descriptions, tap target sizing, TalkBack verification, dynamic type, RTL. Load whenever adding or reviewing a Compose screen.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cc-mobile-android:android-accessibilityThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Every interactive element has:
Every interactive element has:
Modifier.minimumInteractiveComponentSize() on anything that isn't already a Material widget).Text content is already announced. Icons and custom controls need contentDescription or Modifier.semantics.contentDescription = null.Icon(
imageVector = Icons.Default.Delete,
contentDescription = stringResource(R.string.cd_delete_order),
)
Group visually-separate composables into one accessible node when they represent one concept:
Row(
modifier = Modifier
.clickable(onClick = onPlayClick)
.semantics(mergeDescendants = true) {
contentDescription = "Play episode: ${episode.title}"
role = Role.Button
},
) {
AsyncImage(...)
Column { Text(episode.title); Text(episode.duration) }
Icon(Icons.Default.PlayArrow, contentDescription = null) // merged above
}
mergeDescendants = true collapses children into a single accessible node.clearAndSetSemantics { ... } replaces everything underneath — use sparingly.Custom gestures / non-standard widgets must expose state and actions:
Modifier.semantics {
role = Role.Checkbox
stateDescription = if (checked) "selected" else "not selected"
toggleableState = if (checked) ToggleableState.On else ToggleableState.Off
onClick(label = "Toggle") { onToggle(); true }
}
Drag handles, sliders, and carousels benefit from CustomAccessibilityAction so TalkBack users can trigger the same affordance without the gesture.
sp for text — system font scaling respects it.fontScale = 2.0. Test it in previews:@Preview(fontScale = 2.0f, showBackground = true)
@Composable fun OrderRow_LargeFont() = AppTheme { OrderRow(previewOrder) }
wrapContentHeight() or intrinsic sizing.start/end modifiers, never left/right. Compose Modifier API is RTL-aware by default for padding(start = ...), PaddingValues, Arrangement, Alignment.LocalLayoutDirection.current and mirror.LocalLayoutDirection forced to RTL:@Preview @Composable fun OrderRow_Rtl() =
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
AppTheme { OrderRow(previewOrder) }
}
Reading order matches declaration order. If visuals diverge from semantic order, set semantics { traversalIndex = 1f } on the out-of-order node.
For non-visual feedback (error banner, form validation), announce:
LaunchedEffect(errorMessage) {
errorMessage?.let { view.announceForAccessibility(it) }
}
Or use Modifier.semantics { liveRegion = LiveRegionMode.Polite } on the visible error text.
Automated checks complement (don't replace) this. Use Espresso AccessibilityChecks.enable() on UI tests.
contentDescription = "" to suppress. Use null for decorative images.Modifier.clickable on a Text without a role — TalkBack will call it "double-tap to activate" instead of "button".dp text.Modifier.size(32.dp) around a touch target — wrap in minimumInteractiveComponentSize() or size at 48dp minimum.npx claudepluginhub dimitriremoiville/cc-mobile --plugin cc-mobile-androidSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.