首页 / Next.js 16 入门教程 / 无障碍访问

Next.js 16 入门教程

无障碍访问

本教程共 42 篇 · 第 34 篇 · 更新于 2026-07-30 · 约 6 分钟阅读

Next.jsNext.js 16 入门教程无障碍访问a11yARIA屏幕阅读器键盘导航

34. 无障碍访问

本节目标:理解无障碍访问的重要性,掌握 Next.js 内置的 a11y 特性和最佳实践,让所有用户都能使用你的应用。

什么是无障碍访问

无障碍访问(Accessibility,简称 a11y)是指让应用对所有用户都可访问,包括使用屏幕阅读器的视障用户、只能用键盘操作的运动障碍用户、色觉异常的用户等。

Next.js 团队致力于让框架默认就具备良好的无障碍支持。

路由切换通知

页面切换时,屏幕阅读器需要知道页面已经变化。Next.js 默认内置了路由切换通知机制。

自动通知

对于传统的页面跳转(用 <a href>),屏幕阅读器会自动朗读页面标题。

对于 Next.js 的客户端路由(用 <Link> 组件),框架内置了一个路由通知器(Route Announcer)。它会按以下顺序查找要朗读的内容:

  1. document.title
  2. 页面中的 <h1> 元素
  3. URL 路径名

最佳实践:确保每个页面都有唯一且描述性的标题。这是提升无障碍体验最简单有效的方式。

ESLint 无障碍检查

Next.js 内置了 eslint-plugin-jsx-a11y 插件,能在开发阶段捕获常见的无障碍问题。

它检查的内容包括:

  • aria-props:ARIA 属性的拼写是否正确
  • aria-proptypes:ARIA 属性的值是否合法
  • aria-unsupported-elements:在不支持的元素上用了 ARIA
  • role-has-required-aria-props:角色是否包含必需的 ARIA 属性
  • role-supports-aria-props:角色是否支持对应的 ARIA 属性

比如忘记给图片加 alt 属性,ESLint 就会提示你。

键盘导航

很多用户不用鼠标,只靠键盘操作。确保你的应用能通过 Tab 键访问所有交互元素。

焦点管理

// 用 tabIndex 控制焦点顺序
<button tabIndex={0}>可聚焦按钮</button>

// 避免用 tabIndex > 0,会打乱自然顺序
<div tabIndex={1}>不推荐</div>

自定义组件的键盘支持

自定义下拉菜单、模态框等组件需要手动实现键盘交互:

'use client'

export function Dropdown({ items }) {
  const handleKeyDown = (e: React.KeyboardEvent) => {
    switch (e.key) {
      case 'Escape':
        // ESC 关闭下拉菜单
        closeDropdown()
        break
      case 'ArrowDown':
        // 向下箭头移动焦点
        moveFocus(1)
        break
      case 'ArrowUp':
        // 向上箭头移动焦点
        moveFocus(-1)
        break
    }
  }

  return (
    <ul onKeyDown={handleKeyDown} role="listbox">
      {items.map(item => (
        <li key={item.id} role="option" tabIndex={0}>
          {item.label}
        </li>
      ))}
    </ul>
  )
}

ARIA 属性

ARIA(Accessible Rich Internet Applications)是一组 HTML 属性,用来增强组件的无障碍信息。

常用 ARIA 属性

属性用途
role定义元素角色(如 buttonnavigationalert
aria-label提供元素的文本描述
aria-labelledby关联另一个元素作为标签
aria-expanded指示展开/折叠状态
aria-hidden对屏幕阅读器隐藏元素
aria-live声明动态内容区域

示例

// 自定义按钮
<div
  role="button"
  tabIndex={0}
  aria-label="关闭对话框"
  onClick={onClose}
  onKeyDown={e => e.key === 'Enter' && onClose()}
>

</div>

// 导航区域
<nav aria-label="主导航">
  <Link href="/">首页</Link>
  <Link href="/about">关于</Link>
</nav>

// 动态内容通知
<div aria-live="polite">
  {notification && <p>{notification}</p>}
</div>

屏幕阅读器测试

开发完成后,用实际屏幕阅读器测试你的应用:

  • Windows:NVDA(免费)或 JAWS
  • macOS/iOS:VoiceOver(内置)
  • Android:TalkBack(内置)

测试时闭上眼睛,只用听觉和键盘操作,体验视障用户的使用流程。

其他最佳实践

颜色对比度

前景和背景的对比度要足够高。WCAG 2.2 要求正文文本对比度至少 4.5:1,大文本至少 3:1。

可以用浏览器 DevTools 的 Color Contrast Checker 工具检查。

动画与 prefers-reduced-motion

部分用户对动画敏感,系统可以设置”减少动画”偏好。开发时应该尊重这个设置:

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

表单可访问性

// 用 label 关联输入框>
<label htmlFor="email">邮箱</label>
<input id="email" type="email" />

// 错误提示要用 aria-describedBy 关联
<input
  id="email"
  type="email"
  aria-describedby="email-error"
  aria-invalid={hasError}
/>
<span id="email-error" role="alert">
  {errorMessage}
</span>

推荐资源

无障碍访问不是可选项。一个对所有人都友好的应用,才是真正好的应用。