mcpField
W
web-accessibility

web-accessibility

github.com/s-hiraoku设计与 UI1878 次安装3 周前更新
代码仓库

关于 web-accessibility

面向 Web 开发者的无障碍设计技能,提供语义 HTML、键盘操作、ARIA、表单、触控和国际化等实现与审查指导。还包含检查清单,便于系统性检查界面。

主要功能

  1. 01选择语义化 HTML 元素
  2. 02支持键盘导航并保持焦点可见
  3. 03使用 ARIA 标注动态内容
  4. 04为表单提供标签与错误关联
  5. 05检查触控目标和国际化格式
  6. 06通过清单审查无障碍实现

适用场景

  1. 01设计可键盘操作的交互界面
  2. 02检查表单标签与错误提示
  3. 03为动态通知添加屏幕阅读器支持
  4. 04审查移动端触控与焦点样式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/s-hiraoku/synapse-a2a --skill web-accessibility

安装后在 Claude Code 中输入/web-accessibility确认已加载。

详细介绍

Web 无障碍

构建人人都能使用的界面。这些不是可有可无的增强功能,而是基础质量要求。

语义化 HTML

为每项任务使用正确的元素。绝不要用 <div> 模拟交互元素。

// BAD: div with click handler
<div onClick={handleClick} className="button">Submit</div>

// GOOD: semantic button
<button onClick={handleClick}>Submit</button>

// BAD: div as link
<div onClick={() => router.push('/about')}>About</div>

// GOOD: anchor/Link for navigation
<Link href="/about">About</Link>
元素选择指南
用途元素不要使用
执行操作(提交、切换、删除)<button><div onClick>
导航至 URL<a> / <Link><button onClick={navigate}>
表单输入<input>、<select>、<textarea>基于自定义 div 的输入框
分区标题<h1>–<h6>(按顺序)<div className="heading">
项目列表<ul> / <ol> + <li>重复的 <div>
导航组<nav><div className="nav">
主要内容<main><div id="content">

键盘导航

每个交互元素都必须支持键盘访问。

焦点管理
/* NEVER remove focus indicators without replacement */
/* BAD */
*:focus { outline: none; }

/* GOOD: Visible focus only on keyboard navigation */
.interactive:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Group focus for compound controls */
.input-group:focus-within {
  outline: 2px solid var(--color-accent);
}
键盘事件处理
// Interactive custom elements need keyboard support
function CustomButton({ onClick, children }: { onClick: () => void; children: React.ReactNode }) {
  return (
    <div
      role="button"
      tabIndex={0}
      onClick={onClick}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onClick();
        }
      }}
    >
      {children}
    </div>
  );
}
// Better: just use <button> and avoid all of the above
跳过链接

为键盘用户提供跳过导航。

<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50">
  Skip to main content
</a>

作为滚动目标的标题需要为固定页眉设置偏移:

[id] { scroll-margin-top: 5rem; }

ARIA 模式

图标按钮
// Icon-only buttons MUST have aria-label
<button aria-label="Close dialog" onClick={onClose}>
  <XIcon aria-hidden="true" />
</button>

// Decorative icons are hidden from screen readers
<span aria-hidden="true">🔒</span> Secure connection
实时区域

向屏幕阅读器播报动态内容变化。

// Toast notifications
<div role="status" aria-live="polite">
  {notification && <p>{notification.message}</p>}
</div>

// Error alerts
<div role="alert" aria-live="assertive">
  {error && <p>{error.message}</p>}
</div>
加载状态
<button disabled={isLoading} aria-busy={isLoading}>
  {isLoading ? 'Saving\u2026' : 'Save'}  {/* proper ellipsis character */}
</button>

// Skeleton screens
<div aria-busy="true" aria-label="Loading content">
  <Skeleton />
</div>

表单

标签

每个输入框都必须关联一个标签。

// GOOD: Explicit association
<label htmlFor="email">Email</label>
<input id="email" type="email" autoComplete="email" />

// GOOD: Wrapping (clickable label, no htmlFor needed)
<label>
  Email
  <input type="email" autoComplete="email" />
</label>

// GOOD: Visually hidden but accessible
<label htmlFor="search" className="sr-only">Search</label>
<input id="search" type="search" placeholder="Search..." />
输入类型和自动完成

使用语义化输入类型,以启用合适的移动键盘和浏览器行为。

<input type="email" autoComplete="email" />
<input type="tel" autoComplete="tel" />
<input type="url" autoComplete="url" />
<input type="password" autoComplete="current-password" />
<input type="password" autoComplete="new-password" />
验证和错误
<div>
  <label htmlFor="email">Email</label>
  <input
    id="email"
    type="email"
    aria-invalid={!!errors.email}
    aria-describedby={errors.email ? 'email-error' : undefined}
  />
  {errors.email && (
    <p id="email-error" role="alert" className="text-red-600">
      {errors.email}
    </p>
  )}
</div>
表单行为规则
  • 绝不阻止粘贴到任何输入框
  • 对电子邮件地址和代码禁用拼写检查:spellCheck={false}
  • 请求开始前保持提交按钮可用;加载时显示旋转指示器
  • 提交失败时将焦点移至第一个错误
  • 复选框和单选按钮:点击目标区域应为一个整体,标签与输入框之间不能有无法点击的区域

图片和媒体

// Informative images: descriptive alt
<img src="chart.png" alt="Revenue grew 40% from Q1 to Q3 2025" />

// Decorative images: empty alt
<img src="divider.svg" alt="" />

// Prevent layout shift: always set dimensions
<img src="photo.jpg" width={800} height={600} alt="Team photo" />

// Below fold: lazy load
<img src="photo.jpg" loading="lazy" alt="..." />

// Critical: prioritize
<img src="hero.jpg" fetchPriority="high" alt="..." />

触控和移动设备

触控目标

所有交互元素的最小尺寸为 44x44px(WCAG 2.5.5)。

.touch-target {
  min-height: 44px;
  min-width: 44px;
}
安全区域

适配设备刘海和主屏幕指示条。

.full-bleed {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}
触控行为
/* Prevent 300ms delay and highlight flash */
.interactive {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Prevent scroll chaining on modals */
.modal { overscroll-behavior: contain; }

国际化

// BAD: Hardcoded formats
const date = `${month}/${day}/${year}`;
const price = `$${amount.toFixed(2)}`;

// GOOD: Locale-aware formatting
const date = new Intl.DateTimeFormat(locale).format(new Date());
const price = new Intl.NumberFormat(locale, {
  style: 'currency',
  currency: 'USD',
}).format(amount);

// Detect language
const lang = request.headers.get('accept-language')?.split(',')[0] ?? 'en';

提升无障碍性能

  • 列表超过 50 项时:使用虚拟化
  • 关键字体:使用带有 font-display: swap 的 <link rel="preload" as="font">
  • 避免在渲染期间读取布局(这也会导致屏幕阅读器用户体验卡顿)
  • 对大型表单而言,非受控输入的性能优于受控输入

检查清单

用此清单进行检查:

  • 所有交互元素都可通过键盘访问
  • :focus-visible 状态下焦点指示器清晰可见
  • 颜色对比度符合 WCAG AA(正文 4.5:1,大号文本 3:1)
  • 图片具有适当的替代文本
  • 表单输入框具有标签
  • 错误消息通过 aria-describedby 与输入框关联
  • 仅含图标的按钮具有 aria-label
  • 动态内容使用 aria-live 区域
  • 触控目标的最小尺寸为 44x44px
  • 遵循 prefers-reduced-motion 设置
  • 不使用没有替代焦点样式的 outline: none
  • 标题层级按顺序排列(不跳过级别)

评论

支持 Markdown