# Accessibility

> |

- **Type:** Skill
- **Install:** `agentstack add skill-piyushverma0-android-agent-skills-accessibility`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [piyushverma0](https://agentstack.voostack.com/s/piyushverma0)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [piyushverma0](https://github.com/piyushverma0)
- **Source:** https://github.com/piyushverma0/android-agent-skills/tree/main/skills/accessibility
- **Website:** https://android-agent-skills.vercel.app

## Install

```sh
agentstack add skill-piyushverma0-android-agent-skills-accessibility
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 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

```kotlin
// ✅ 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

```kotlin
// ✅ 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

```kotlin
// ✅ 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

```kotlin
// ✅ 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

```kotlin
// ✅ 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

```kotlin
// ✅ 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

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [piyushverma0](https://github.com/piyushverma0)
- **Source:** [piyushverma0/android-agent-skills](https://github.com/piyushverma0/android-agent-skills)
- **License:** MIT
- **Homepage:** https://android-agent-skills.vercel.app

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-piyushverma0-android-agent-skills-accessibility
- Seller: https://agentstack.voostack.com/s/piyushverma0
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
