agentsclimarketplace

Accessibility

Skill piyushverma0/android-agent-skills/skills/accessibility

27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.

Install
npx -y skills add piyushverma0/android-agent-skills --skill accessibility

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 14 stars14 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Android accessibility for AI agents. Use this skill whenever implementing accessibility, TalkBack support, content descriptions, semantic properties, touch target sizes, contrast ratios, screen reader support, contentDescription, semantics modifier, clearAndSetSemantics, mergeDescendants, heading, stateDescription, traversalOrder, custom actions, live regions, accessibility service, WCAG compliance, accessibility audit, or any Compose accessibility. Required for Play Store submission in many markets. Always apply before shipping any screen.

SKILL.md

6.4 KB, as published. Nobody here has run it

Android Accessibility

Accessibility is not optional — it's required by law in many markets and checked by the Play Store.

Rule 1: Content descriptions for all interactive elements

// ✅ Every non-text interactive element needs contentDescription
IconButton(onClick = onLike) {
    Icon(
        imageVector = if (isLiked) Icons.Filled.Favorite else Icons.Outlined.FavoriteBorder,
        contentDescription = if (isLiked) "Remove from favorites" else "Add to favorites"
        // Describe the ACTION, not the icon name
    )
}

// ✅ Image with context
AsyncImage(
    model = imageUrl,
    contentDescription = "$productName product image"  // meaningful description
)

// ✅ Decorative images — explicitly null
Icon(Icons.Default.Star, contentDescription = null)  // decorative, screen reader skips it

// ❌ Generic description
Icon(Icons.Default.Favorite, contentDescription = "icon")  // useless to screen reader

Rule 2: Minimum touch target — 48×48dp

// ✅ Ensure minimum touch target size
@Composable
fun SmallIconButton(onClick: () -> Unit, icon: ImageVector, contentDescription: String) {
    Box(
        modifier = Modifier
            .size(48.dp)                     // minimum touch target
            .clickable(
                onClick = onClick,
                interactionSource = remember { MutableInteractionSource() },
                indication = rememberRipple(bounded = false)
            ),
        contentAlignment = Alignment.Center
    ) {
        Icon(
            imageVector = icon,
            contentDescription = contentDescription,
            modifier = Modifier.size(24.dp)  // visual size separate from touch target
        )
    }
}

// ✅ Or use minimumInteractiveComponentSize modifier
Icon(
    imageVector = icon,
    contentDescription = description,
    modifier = Modifier.minimumInteractiveComponentSize()  // auto 48dp touch target
)

Rule 3: Semantic properties for complex components

// ✅ Card — merge all content for TalkBack
@Composable
fun ProductCard(product: Product, onAddToCart: () -> Unit) {
    Card(
        modifier = Modifier.semantics(mergeDescendants = true) {}
        // TalkBack reads all child text as one item: "Blue T-Shirt, $29.99, In stock"
    ) {
        Column {
            Text(product.name)
            Text("$${product.price}")
            Text(if (product.inStock) "In stock" else "Out of stock")
            Button(onClick = onAddToCart) { Text("Add to cart") }
        }
    }
}

// ✅ Custom state description
Switch(
    checked = isEnabled,
    onCheckedChange = onToggle,
    modifier = Modifier.semantics {
        stateDescription = if (isEnabled) "Notifications on" else "Notifications off"
    }
)

// ✅ Mark as heading for navigation
Text(
    text = "Today's Orders",
    style = MaterialTheme.typography.titleLarge,
    modifier = Modifier.semantics { heading() }
)

// ✅ Custom action — add actions TalkBack can trigger
LazyColumn {
    items(items) { item ->
        SwipeableItemCard(
            item = item,
            modifier = Modifier.semantics {
                customActions = listOf(
                    CustomAccessibilityAction("Delete ${item.title}") {
                        onDelete(item.id)
                        true
                    },
                    CustomAccessibilityAction("Archive ${item.title}") {
                        onArchive(item.id)
                        true
                    }
                )
            }
        )
    }
}

Rule 4: Color contrast — WCAG AA minimum

// ✅ Use Material 3 color roles — contrast is guaranteed by the spec
Text(
    text = "Important message",
    color = MaterialTheme.colorScheme.onSurface   // guaranteed contrast on Surface
)

// ✅ Check custom colors with contrast tool
// onSurface on Surface: contrast ratio ≥ 4.5:1 (WCAG AA)
// onPrimary on Primary: contrast ratio ≥ 4.5:1

// ❌ Custom colors without contrast check
Text(text = "Label", color = Color(0xFF888888))   // gray on white = likely fails WCAG

Rule 5: Screen reader traversal order

// ✅ Control traversal order for complex layouts
@Composable
fun ProductHeader(name: String, price: String, rating: Float) {
    Row(modifier = Modifier.semantics { isTraversalGroup = true }) {
        Column(modifier = Modifier.weight(1f)) {
            Text(name, modifier = Modifier.semantics { traversalIndex = 0f })
            Text(price, modifier = Modifier.semantics { traversalIndex = 1f })
        }
        RatingBar(rating, modifier = Modifier.semantics { traversalIndex = 2f })
    }
}

Rule 6: Live regions for dynamic content

// ✅ Announce changes to screen reader users
Text(
    text = "Found ${results.size} results",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Polite  // announced when content changes, doesn't interrupt
    }
)

// LiveRegionMode.Assertive — interrupts current speech (use sparingly)
Text(
    text = errorMessage,
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Assertive  // for critical errors only
    }
)

Accessibility checklist (run before every release)

  • All interactive elements have meaningful contentDescription
  • All touch targets are ≥ 48×48dp
  • Cards and list items use mergeDescendants = true
  • Color-only information has a text/shape alternative
  • All text passes 4.5:1 contrast ratio (WCAG AA)
  • Headings marked with heading() semantic
  • Swipeable actions have customActions for TalkBack users
  • Dynamic content uses live regions
  • Tested with TalkBack enabled and touch exploration

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.