构建无障碍组件之Carousel Pattern

0 阅读8分钟

Carousel Pattern 详解:构建无障碍轮播组件

轮播(Carousel)是一种按顺序展示一组内容项(称为幻灯片)的组件。本文基于 W3C WAI-ARIA Carousel Pattern 规范,详解如何构建无障碍的轮播组件。

一、Carousel 的定义与核心概念

1.1 什么是 Carousel

Carousel(也称为幻灯片或图片轮播器)具有以下特征:

  • 展示一组称为**幻灯片(Slide)**的内容项
  • 通常一次显示一个幻灯片,通过控制按钮切换
  • 可以自动轮播,也可以手动控制
  • 幻灯片可以包含任何类型的内容,图片轮播最为常见

1.2 核心术语

术语说明
Slide轮播中的单个内容容器
Rotation Control停止/启动自动轮播的交互控件
Next Slide Control显示下一张幻灯片的控件(通常为箭头样式)
Previous Slide Control显示上一张幻灯片的控件(通常为箭头样式)
Slide Picker Controls选择特定幻灯片的控件组(通常为圆点样式)
┌─────────────────────────────────────────────────────────────┐
│  Carousel (role="region" + aria-roledescription)            │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                                                     │    │
│  │              ┌─────────────────┐                    │    │
│  │              │    Slide 1      │                    │    │
│  │              │  ┌───────────┐  │                    │    │
│  │              │  │           │  │                    │    │
│  │              │  │  Image /  │  │  <-- Current       │    │
│  │              │  │  Content  │  │      Slide         │    │
│  │              │  │           │  │                    │    │
│  │              │  └───────────┘  │                    │    │
│  │              └─────────────────┘                    │    │
│  │                                                     │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │  Slide 2 (hidden)    │   Slide 3 (hidden)   │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  │                                                     │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│     <-- Prev        o O O        Next -->                   │
│           (Slide Picker / Dots Navigation)                  │
│                                                             │
│                    [ Pause/Play ]                           │
└─────────────────────────────────────────────────────────────┘

1.3 无障碍挑战

轮播组件如果没有正确实现,会对无障碍体验造成严重影响:

  • 屏幕阅读器用户困惑:如果不可见的幻灯片没有被正确隐藏,用户可能在不知情的情况下从幻灯片 1 跳转到幻灯片 2 的内容
  • 自动轮播干扰:自动轮播可能打断屏幕阅读器用户的浏览流程
  • 键盘导航困难:如果轮播没有正确处理焦点管理,键盘用户可能无法有效控制轮播

二、WAI-ARIA 角色与属性

2.1 基本角色

轮播区域使用 role="region" 标记为地标区域,并通过 aria-roledescription="carousel" 提供额外的角色描述:

<section
  role="region"
  aria-roledescription="carousel"
  aria-label="产品展示">
  <!-- 轮播内容 -->
</section>

2.2 幻灯片属性

每个幻灯片具有以下属性:

<div
  role="group"
  aria-roledescription="slide"
  aria-label="第 1 张,共 3 张">
  <!-- 幻灯片内容 -->
</div>

2.3 幻灯片可见性

使用 aria-hidden 控制幻灯片的可见性:

  • aria-hidden="true":幻灯片不可见(不在视口内)
  • aria-hidden="false":幻灯片可见(当前显示的幻灯片)
<!-- 当前显示的幻灯片 -->
<div
  role="group"
  aria-roledescription="slide"
  aria-label="第 1 张,共 3 张"
  aria-hidden="false">
  <img
    src="slide1.jpg"
    alt="产品图片 1" />
</div>

<!-- 隐藏的幻灯片 -->
<div
  role="group"
  aria-roledescription="slide"
  aria-label="第 2 张,共 3 张"
  aria-hidden="true">
  <img
    src="slide2.jpg"
    alt="产品图片 2" />
</div>

2.4 控制按钮属性

上一张/下一张按钮

使用 aria-controls 属性指向被控制的幻灯片容器 ID,让辅助技术用户了解按钮会影响哪个区域的内容:

<button
  aria-label="上一张"
  aria-controls="carousel-slides"></button>
<button
  aria-label="下一张"
  aria-controls="carousel-slides"></button>
轮播控制按钮(停止/启动)

使用 aria-pressed 表示按钮的按下状态,false 表示轮播正在运行,点击后会停止:

<button
  aria-label="停止轮播"
  aria-pressed="false"
  aria-controls="carousel-slides"></button>
幻灯片选择器(圆点导航)

幻灯片选择器使用 role="tab" 模式,每个圆点按钮都是一个 tab,通过 aria-selected 表示当前选中的幻灯片:

<div
  role="tablist"
  aria-label="幻灯片选择">
  <button
    role="tab"
    aria-label="第 1 张"
    aria-selected="true"
    aria-controls="slide-1"></button>
  <button
    role="tab"
    aria-label="第 2 张"
    aria-selected="false"
    aria-controls="slide-2"></button>
  <button
    role="tab"
    aria-label="第 3 张"
    aria-selected="false"
    aria-controls="slide-3"></button>
</div>

三、键盘交互规范

3.1 基本键盘交互

按键功能
Tab / Shift + Tab在轮播的交互元素之间移动焦点
Space / Enter激活按钮(上一张、下一张、停止/启动)
方向键(可选)如果幻灯片选择器使用 Tab 模式,可用方向键切换幻灯片

3.2 自动轮播的键盘行为

  • 当轮播中的任何元素获得键盘焦点时,自动轮播必须停止
  • 轮播不会自动恢复,除非用户明确激活旋转控制

3.3 Tab 顺序

  • 旋转控制按钮(如果存在)必须是轮播内部 Tab 顺序中的第一个元素
  • 这确保辅助技术用户可以轻松找到控制按钮

四、鼠标交互规范

4.1 悬停行为

  • 当鼠标悬停在轮播上时,自动轮播必须停止
  • 鼠标移出后,可以恢复自动轮播(根据设计决定)

4.2 点击行为

  • 点击上一张/下一张按钮切换幻灯片
  • 点击幻灯片选择器跳转到特定幻灯片
  • 点击旋转控制按钮停止/启动自动轮播

五、实现方式

5.1 基础轮播结构

<section
  class="carousel"
  aria-roledescription="carousel"
  aria-label="产品展示">
  <!-- 旋转控制按钮 -->
  <button
    class="rotation-control"
    aria-label="停止轮播"
    aria-pressed="false"
    aria-controls="carousel-slides"></button>

  <!-- 幻灯片容器 -->
  <div
    id="carousel-slides"
    class="carousel-slides">
    <div
      role="group"
      aria-roledescription="slide"
      aria-label="第 1 张,共 3 张"
      aria-hidden="false"
      class="slide active">
      <img
        src="slide1.jpg"
        alt="产品图片 1" />
      <div class="slide-content">
        <h2>产品标题 1</h2>
        <p>产品描述...</p>
        <a href="/product1">了解更多</a>
      </div>
    </div>

    <div
      role="group"
      aria-roledescription="slide"
      aria-label="第 2 张,共 3 张"
      aria-hidden="true"
      class="slide">
      <img
        src="slide2.jpg"
        alt="产品图片 2" />
      <div class="slide-content">
        <h2>产品标题 2</h2>
        <p>产品描述...</p>
        <a href="/product2">了解更多</a>
      </div>
    </div>

    <div
      role="group"
      aria-roledescription="slide"
      aria-label="第 3 张,共 3 张"
      aria-hidden="true"
      class="slide">
      <img
        src="slide3.jpg"
        alt="产品图片 3" />
      <div class="slide-content">
        <h2>产品标题 3</h2>
        <p>产品描述...</p>
        <a href="/product3">了解更多</a>
      </div>
    </div>
  </div>

  <!-- 导航按钮 -->
  <button
    class="prev-btn"
    aria-label="上一张"
    aria-controls="carousel-slides"></button>
  <button
    class="next-btn"
    aria-label="下一张"
    aria-controls="carousel-slides"></button>

  <!-- 幻灯片选择器 -->
  <div
    class="slide-picker"
    role="tablist"
    aria-label="幻灯片选择">
    <button
      role="tab"
      aria-label="第 1 张"
      aria-selected="true"
      aria-controls="slide-1"></button>
    <button
      role="tab"
      aria-label="第 2 张"
      aria-selected="false"
      aria-controls="slide-2"></button>
    <button
      role="tab"
      aria-label="第 3 张"
      aria-selected="false"
      aria-controls="slide-3"></button>
  </div>
</section>

5.2 关键实现要点

幻灯片可见性管理:

  • 当前显示的幻灯片:aria-hidden="false"
  • 隐藏的幻灯片:aria-hidden="true"
  • 使用 CSS 控制显示/隐藏(如 opacitydisplayvisibility

自动轮播控制:

  • 键盘焦点进入轮播区域时,必须停止自动轮播
  • 鼠标悬停在轮播上时,必须停止自动轮播
  • 提供停止/启动按钮,让用户控制自动轮播

幻灯片选择器状态:

  • 当前幻灯片对应的按钮:aria-selected="true"
  • 其他按钮:aria-selected="false"

六、常见应用场景

6.1 产品图片展示

<section
  aria-roledescription="carousel"
  aria-label="产品图片">
  <div
    role="group"
    aria-roledescription="slide"
    aria-label="第 1 张,共 4 张">
    <img
      src="product-1.jpg"
      alt="产品正面视图" />
  </div>
  <div
    role="group"
    aria-roledescription="slide"
    aria-label="第 2 张,共 4 张">
    <img
      src="product-2.jpg"
      alt="产品侧面视图" />
  </div>
  <div
    role="group"
    aria-roledescription="slide"
    aria-label="第 3 张,共 4 张">
    <img
      src="product-3.jpg"
      alt="产品背面视图" />
  </div>
  <div
    role="group"
    aria-roledescription="slide"
    aria-label="第 4 张,共 4 张">
    <img
      src="product-4.jpg"
      alt="产品细节视图" />
  </div>
</section>

6.2 testimonials/客户评价

<section
  aria-roledescription="carousel"
  aria-label="客户评价">
  <div
    role="group"
    aria-roledescription="slide"
    aria-label="第 1 张,共 3 张">
    <blockquote>
      <p>"这个产品改变了我的工作方式..."</p>
      <footer>— 张三,某公司员工</footer>
    </blockquote>
  </div>
  <div
    role="group"
    aria-roledescription="slide"
    aria-label="第 2 张,共 3 张">
    <blockquote>
      <p>"客服非常专业,响应迅速..."</p>
      <footer>— 李四,自由职业者</footer>
    </blockquote>
  </div>
</section>

6.3 新闻/公告轮播

<section
  aria-roledescription="carousel"
  aria-label="最新公告">
  <div
    role="group"
    aria-roledescription="slide"
    aria-label="第 1 张,共 3 张">
    <article>
      <h3>公司发布新产品</h3>
      <p>我们很高兴地宣布...</p>
      <a href="/news/1">阅读更多</a>
    </article>
  </div>
</section>

七、最佳实践

7.1 始终提供轮播控制

  • 必须提供上一张/下一张按钮
  • 如果启用自动轮播,必须提供停止/启动控制按钮
  • 建议提供幻灯片选择器(圆点导航)

7.2 正确处理幻灯片可见性

  • 使用 aria-hidden="true" 隐藏不可见的幻灯片
  • 确保隐藏的幻灯片内容不会被屏幕阅读器读取
  • 当前幻灯片使用 aria-hidden="false"

7.3 自动轮播的控制

  • 自动轮播必须在以下情况下停止:
    • 键盘焦点进入轮播区域
    • 鼠标悬停在轮播上
    • 用户点击停止按钮
  • 不要在用户未明确请求的情况下重新启动自动轮播

7.4 提供清晰的标签

  • 为轮播区域提供描述性的 aria-label
  • 为每个幻灯片提供包含位置信息的标签(如"第 1 张,共 3 张")
  • 为所有控制按钮提供清晰的 aria-label

7.5 避免使用轮播的情况

以下情况不建议使用轮播:

  • 内容对用户都很重要,需要同时可见
  • 用户需要比较不同幻灯片的内容
  • 幻灯片内容包含重要的交互元素

在这些情况下,考虑使用静态列表或网格布局。

7.6 移动端触摸支持

移动端应支持触摸滑动切换幻灯片:

  • 向左滑动 → 显示下一张
  • 向右滑动 → 显示上一张
  • 需要设置滑动阈值(如 50px),避免误触

八、总结

构建无障碍的 Carousel 组件需要特别关注:

  1. 正确的 ARIA 标记:使用 role="region"aria-roledescriptionaria-hidden 等属性
  2. 完整的键盘支持:确保所有功能都可以通过键盘访问
  3. 自动轮播控制:提供停止/启动控制,并在焦点进入时自动停止
  4. 清晰的标签:为轮播、幻灯片和控制按钮提供描述性标签
  5. 幻灯片可见性管理:正确隐藏不可见的幻灯片,避免屏幕阅读器混淆

轮播组件虽然常见,但如果没有正确实现,会对无障碍体验造成严重影响。遵循 W3C Carousel Pattern 规范,我们能够创建既美观又包容的轮播组件,为所有用户提供良好的体验。

文章同步于 an-Onion 的 Github。码字不易,欢迎点赞。