library/specializations/mobile-development/skills/accessibility-testing/SKILL.md
Mobile accessibility testing skill for WCAG compliance, VoiceOver/TalkBack validation, dynamic type support, color contrast analysis, and accessibility auditing across iOS and Android platforms.
npx skillsauth add a5c-ai/babysitter accessibility-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive mobile accessibility testing and validation for iOS and Android platforms, ensuring WCAG 2.1/2.2 compliance and optimal screen reader compatibility.
This skill provides capabilities for testing mobile application accessibility, including screen reader compatibility, dynamic type support, color contrast validation, and compliance with Web Content Accessibility Guidelines (WCAG) adapted for mobile platforms.
# Accessibility testing tools
xcode-select --install
# UI testing with accessibility focus
pod 'ViewInspector' # SwiftUI testing
// build.gradle
dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-accessibility:3.5.1'
}
# Accessibility testing CLI tools
npm install -g @axe-core/cli
pip install accessibility-checker
import SwiftUI
struct AccessibleButton: View {
var body: some View {
Button(action: { /* action */ }) {
Image(systemName: "heart.fill")
}
.accessibilityLabel("Add to favorites")
.accessibilityHint("Double tap to add this item to your favorites list")
.accessibilityAddTraits(.isButton)
}
}
struct AccessibleList: View {
var body: some View {
List {
ForEach(items) { item in
ItemRow(item: item)
.accessibilityElement(children: .combine)
.accessibilityLabel("\(item.title), \(item.subtitle)")
.accessibilityValue(item.isSelected ? "Selected" : "Not selected")
}
}
.accessibilityIdentifier("items_list")
}
}
import UIKit
class AccessibleViewController: UIViewController {
func configureAccessibility() {
// Basic label
button.accessibilityLabel = "Submit order"
button.accessibilityHint = "Double tap to submit your order"
// Grouped elements
containerView.isAccessibilityElement = true
containerView.accessibilityLabel = "Order summary: 3 items, total $45.99"
// Custom actions
cell.accessibilityCustomActions = [
UIAccessibilityCustomAction(name: "Delete", target: self, selector: #selector(deleteItem)),
UIAccessibilityCustomAction(name: "Edit", target: self, selector: #selector(editItem))
]
}
}
import androidx.compose.ui.semantics.*
@Composable
fun AccessibleButton() {
IconButton(
onClick = { /* action */ },
modifier = Modifier.semantics {
contentDescription = "Add to favorites"
role = Role.Button
}
) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
}
@Composable
fun AccessibleCard(item: Item) {
Card(
modifier = Modifier.semantics(mergeDescendants = true) {
contentDescription = "${item.title}, ${item.subtitle}"
stateDescription = if (item.isSelected) "Selected" else "Not selected"
}
) {
// Card content
}
}
import android.view.View
import android.view.accessibility.AccessibilityNodeInfo
class AccessibleActivity : AppCompatActivity() {
fun configureAccessibility() {
// Basic content description
imageButton.contentDescription = "Add to favorites"
// Important for accessibility
decorativeImage.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
// Live regions for dynamic content
statusTextView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE
// Custom accessibility delegate
customView.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
super.onInitializeAccessibilityNodeInfo(host, info)
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)
info.contentDescription = "Custom description"
}
}
}
}
// iOS - Check contrast ratio
import UIKit
func calculateContrastRatio(foreground: UIColor, background: UIColor) -> Double {
let fgLuminance = relativeLuminance(foreground)
let bgLuminance = relativeLuminance(background)
let lighter = max(fgLuminance, bgLuminance)
let darker = min(fgLuminance, bgLuminance)
return (lighter + 0.05) / (darker + 0.05)
}
func relativeLuminance(_ color: UIColor) -> Double {
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: nil)
let transform: (CGFloat) -> Double = { value in
let v = Double(value)
return v <= 0.03928 ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4)
}
return 0.2126 * transform(r) + 0.7152 * transform(g) + 0.0722 * transform(b)
}
// Usage
let ratio = calculateContrastRatio(foreground: .label, background: .systemBackground)
let meetsWCAGAA = ratio >= 4.5 // Normal text
let meetsWCAGAAA = ratio >= 7.0 // Enhanced
import XCTest
class AccessibilityTests: XCTestCase {
func testVoiceOverNavigation() {
let app = XCUIApplication()
app.launch()
// Verify accessibility elements exist
XCTAssertTrue(app.buttons["Submit order"].exists)
XCTAssertTrue(app.staticTexts["Order total"].exists)
// Check accessibility traits
let submitButton = app.buttons["Submit order"]
XCTAssertTrue(submitButton.isEnabled)
// Navigate with VoiceOver gestures (simulated)
let elements = app.descendants(matching: .any).allElementsBoundByAccessibilityElement
XCTAssertGreaterThan(elements.count, 0)
}
func testDynamicTypeSupport() {
let app = XCUIApplication()
app.launchArguments = ["-UIPreferredContentSizeCategoryName", "UICTContentSizeCategoryAccessibilityXXL"]
app.launch()
// Verify layout doesn't break at large text sizes
XCTAssertTrue(app.staticTexts["Title"].exists)
XCTAssertFalse(app.staticTexts["Title"].frame.isEmpty)
}
}
import androidx.test.espresso.accessibility.AccessibilityChecks
import org.junit.BeforeClass
class AccessibilityTest {
companion object {
@BeforeClass
@JvmStatic
fun enableAccessibilityChecks() {
AccessibilityChecks.enable()
.setRunChecksFromRootView(true)
}
}
@Test
fun testScreenAccessibility() {
onView(withId(R.id.main_layout))
.check(matches(isDisplayed()))
// Automatic accessibility checks run on every view interaction
onView(withId(R.id.submit_button))
.perform(click())
}
}
const accessibilityTestTask = defineTask({
name: 'accessibility-testing',
description: 'Test mobile app accessibility compliance',
inputs: {
platform: { type: 'string', required: true, enum: ['ios', 'android', 'both'] },
wcagLevel: { type: 'string', required: true, enum: ['A', 'AA', 'AAA'] },
projectPath: { type: 'string', required: true },
testScreens: { type: 'array', items: { type: 'string' } }
},
outputs: {
complianceReport: { type: 'object' },
violations: { type: 'array' },
recommendations: { type: 'array' },
score: { type: 'number' }
},
async run(inputs, taskCtx) {
return {
kind: 'skill',
title: `Test ${inputs.wcagLevel} accessibility for ${inputs.platform}`,
skill: {
name: 'accessibility-testing',
context: {
operation: 'audit',
platform: inputs.platform,
wcagLevel: inputs.wcagLevel,
projectPath: inputs.projectPath,
screens: inputs.testScreens
}
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/result.json`
}
};
}
});
{
"mcpServers": {
"axiom": {
"command": "npx",
"args": ["axiom-mcp"],
"env": {
"XCODE_PROJECT": "/path/to/project.xcodeproj"
}
}
}
}
a11y_audit_ios - Run iOS Accessibility Inspector audita11y_audit_android - Run Android Accessibility Scannercheck_contrast_ratio - Validate color contrastvalidate_touch_targets - Check touch target sizestest_screen_reader - Simulate screen reader navigationgenerate_a11y_report - Create compliance reportdevelopment
Model documentation skill for generating model cards following Google's model card framework.
development
MLflow integration skill for experiment tracking, model registry, and artifact management. Enables LLMs to log experiments, compare runs, manage model lifecycle, and retrieve artifacts through the MLflow API.
data-ai
LIME-based local explanation skill for individual predictions across tabular, text, and image data.
devops
Kubeflow Pipelines skill for ML workflow orchestration, component management, and Kubernetes-native ML.