移动端无障碍测试技能Skill accessibility-testing

移动端无障碍测试技能是一个专注于iOS和Android平台应用无障碍性验证的专业工具集。它提供全面的WCAG合规性测试、屏幕阅读器兼容性验证、动态类型支持检查、颜色对比度分析等功能,帮助开发者确保应用对所有用户(包括残障人士)都可访问。关键词:移动应用无障碍测试、WCAG合规性、VoiceOver验证、TalkBack测试、颜色对比度分析、iOS无障碍、Android无障碍、屏幕阅读器兼容性、触摸目标验证、无障碍审计。

测试 0 次安装 0 次浏览 更新于 2/25/2026

name: accessibility-testing description: 用于WCAG合规性的移动端无障碍测试技能,包含VoiceOver/TalkBack验证、动态类型支持、颜色对比度分析以及跨iOS和Android平台的无障碍审计。 allowed-tools: Read, Grep, Write, Bash, Edit, Glob, WebFetch

无障碍测试技能

为iOS和Android平台提供全面的移动应用无障碍测试和验证,确保符合WCAG 2.1/2.2标准并提供最佳的屏幕阅读器兼容性。

概述

本技能提供测试移动应用无障碍性的能力,包括屏幕阅读器兼容性、动态类型支持、颜色对比度验证以及针对移动平台适配的Web内容无障碍指南(WCAG)合规性检查。

能力

屏幕阅读器测试

  • 验证VoiceOver兼容性(iOS)
  • 测试TalkBack交互(Android)
  • 验证无障碍标签和提示
  • 检查阅读顺序和焦点导航
  • 测试自定义无障碍操作

动态类型支持

  • 验证iOS动态类型缩放
  • 测试Android字体缩放偏好
  • 检查极端尺寸下的布局适应性
  • 验证文本截断处理
  • 测试多行文本换行

颜色对比度分析

  • 测量对比度比率(WCAG AA/AAA)
  • 识别低对比度文本和UI元素
  • 针对浅色/深色模式主题进行验证
  • 检查色盲无障碍性
  • 建议合规的颜色替代方案

无障碍审计

  • 运行iOS无障碍检查器审计
  • 执行Android无障碍扫描器
  • 生成合规性报告
  • 识别WCAG违规
  • 优先处理修复工作

触摸目标验证

  • 测量触摸目标尺寸(iOS最小44x44pt,Android最小48x48dp)
  • 检查交互元素之间的间距
  • 验证基于手势的交互
  • 为复杂手势测试单点触控替代方案

先决条件

iOS开发

# 无障碍测试工具
xcode-select --install

# 带无障碍焦点的UI测试
pod 'ViewInspector'  # SwiftUI测试

Android开发

// build.gradle
dependencies {
    androidTestImplementation 'androidx.test.espresso:espresso-accessibility:3.5.1'
}

测试工具

# 无障碍测试CLI工具
npm install -g @axe-core/cli
pip install accessibility-checker

使用模式

iOS无障碍标签(SwiftUI)

import SwiftUI

struct AccessibleButton: View {
    var body: some View {
        Button(action: { /* 操作 */ }) {
            Image(systemName: "heart.fill")
        }
        .accessibilityLabel("添加到收藏")
        .accessibilityHint("双击将此项目添加到您的收藏列表")
        .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 ? "已选中" : "未选中")
            }
        }
        .accessibilityIdentifier("items_list")
    }
}

iOS无障碍标签(UIKit)

import UIKit

class AccessibleViewController: UIViewController {
    func configureAccessibility() {
        // 基础标签
        button.accessibilityLabel = "提交订单"
        button.accessibilityHint = "双击提交您的订单"

        // 分组元素
        containerView.isAccessibilityElement = true
        containerView.accessibilityLabel = "订单摘要:3件商品,总计$45.99"

        // 自定义操作
        cell.accessibilityCustomActions = [
            UIAccessibilityCustomAction(name: "删除", target: self, selector: #selector(deleteItem)),
            UIAccessibilityCustomAction(name: "编辑", target: self, selector: #selector(editItem))
        ]
    }
}

Android无障碍(Jetpack Compose)

import androidx.compose.ui.semantics.*

@Composable
fun AccessibleButton() {
    IconButton(
        onClick = { /* 操作 */ },
        modifier = Modifier.semantics {
            contentDescription = "添加到收藏"
            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) "已选中" else "未选中"
        }
    ) {
        // 卡片内容
    }
}

Android无障碍(XML视图)

import android.view.View
import android.view.accessibility.AccessibilityNodeInfo

class AccessibleActivity : AppCompatActivity() {
    fun configureAccessibility() {
        // 基础内容描述
        imageButton.contentDescription = "添加到收藏"

        // 对无障碍不重要
        decorativeImage.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO

        // 动态内容的实时区域
        statusTextView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE

        // 自定义无障碍代理
        customView.accessibilityDelegate = object : View.AccessibilityDelegate() {
            override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
                super.onInitializeAccessibilityNodeInfo(host, info)
                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)
                info.contentDescription = "自定义描述"
            }
        }
    }
}

颜色对比度验证

// iOS - 检查对比度比率
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)
}

// 使用
let ratio = calculateContrastRatio(foreground: .label, background: .systemBackground)
let meetsWCAGAA = ratio >= 4.5  // 普通文本
let meetsWCAGAAA = ratio >= 7.0 // 增强

无障碍测试(XCTest)

import XCTest

class AccessibilityTests: XCTestCase {
    func testVoiceOverNavigation() {
        let app = XCUIApplication()
        app.launch()

        // 验证无障碍元素存在
        XCTAssertTrue(app.buttons["提交订单"].exists)
        XCTAssertTrue(app.staticTexts["订单总计"].exists)

        // 检查无障碍特性
        let submitButton = app.buttons["提交订单"]
        XCTAssertTrue(submitButton.isEnabled)

        // 使用VoiceOver手势导航(模拟)
        let elements = app.descendants(matching: .any).allElementsBoundByAccessibilityElement
        XCTAssertGreaterThan(elements.count, 0)
    }

    func testDynamicTypeSupport() {
        let app = XCUIApplication()
        app.launchArguments = ["-UIPreferredContentSizeCategoryName", "UICTContentSizeCategoryAccessibilityXXL"]
        app.launch()

        // 验证布局在大文本尺寸下不会破坏
        XCTAssertTrue(app.staticTexts["标题"].exists)
        XCTAssertFalse(app.staticTexts["标题"].frame.isEmpty)
    }
}

无障碍测试(Espresso)

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()))

        // 每次视图交互都会自动运行无障碍检查
        onView(withId(R.id.submit_button))
            .perform(click())
    }
}

与Babysitter SDK集成

任务定义示例

const accessibilityTestTask = defineTask({
  name: 'accessibility-testing',
  description: '测试移动应用无障碍合规性',

  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: `测试${inputs.platform}的${inputs.wcagLevel}级无障碍性`,
      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`
      }
    };
  }
});

MCP服务器集成

使用Axiom无障碍(iOS)

{
  "mcpServers": {
    "axiom": {
      "command": "npx",
      "args": ["axiom-mcp"],
      "env": {
        "XCODE_PROJECT": "/path/to/project.xcodeproj"
      }
    }
  }
}

可用的MCP工具

  • a11y_audit_ios - 运行iOS无障碍检查器审计
  • a11y_audit_android - 运行Android无障碍扫描器
  • check_contrast_ratio - 验证颜色对比度
  • validate_touch_targets - 检查触摸目标尺寸
  • test_screen_reader - 模拟屏幕阅读器导航
  • generate_a11y_report - 创建合规性报告

WCAG 2.1移动端检查清单

可感知性

  • [ ] 为非文本内容提供文本替代(1.1.1)
  • [ ] 音频内容提供字幕(1.2.2)
  • [ ] 颜色不是传达信息的唯一方式(1.4.1)
  • [ ] 普通文本对比度比率≥4.5:1(1.4.3)
  • [ ] 文本可调整至200%(1.4.4)
  • [ ] 无需水平滚动即可重排(1.4.10)

可操作性

  • [ ] 触摸目标≥44x44pt/48x48dp(2.5.5)
  • [ ] 提供单指针手势(2.5.1)
  • [ ] 操作不需要运动(2.5.4)
  • [ ] 方向未锁定(1.3.4)
  • [ ] 键盘用户的焦点可见(2.4.7)

可理解性

  • [ ] 页面语言可编程确定(3.1.1)
  • [ ] 一致的导航模式(3.2.3)
  • [ ] 错误识别清晰(3.3.1)
  • [ ] 用户输入的标签或说明(3.3.2)

健壮性

  • [ ] 辅助技术的有效标记(4.1.1)
  • [ ] 名称、角色、值可用(4.1.2)
  • [ ] 状态消息向屏幕阅读器宣布(4.1.3)

最佳实践

  1. 与真实用户测试:在测试中包括残障用户
  2. 使用原生控件:利用平台无障碍功能
  3. 在极端情况下测试:检查最大的动态类型和字体缩放
  4. 验证阅读顺序:确保逻辑焦点导航
  5. 提供文本替代:标记所有图像和图标
  6. 支持多种输入方法:触摸、语音、开关控制

参考资料