Skip to content

网页无障碍辅助插件

A11y 是一个轻量、独立、可无缝集成到任何网页的无障碍工具栏。它通过浮动按钮提供 20 余项辅助功能,覆盖视觉、运动、认知与听觉障碍,帮助您快速提升网站的可访问性,并满足 WCAG 2.1 合规要求。

🚀 快速开始

bash
yarn add @william-xu-web/c-a11y
bash
npm install @william-xu-web/c-a11y
html
<!-- 在 HTML 中引入脚本,脚本从 node_modules 目录下的 c-a11y.min.js 文件引入 -->
<script src="/path/to/c-a11y.min.js" defer></script>
javascript
import { init } from "@william-xu-web/c-a11y";

const widget = init({
  lang: "zh",
  position: "bottom-right",
});

// 需要时销毁插件并清理所有事件监听
widget.destroy();

瑶光代码仓

瑶光代码仓名称:demo-a11y。

✨ 功能全景

所有功能均按需启用,用户可随时开关,偏好设置自动保存至本地。

👁️ 视觉与显示辅助

功能WCAG 参考适用人群
字体缩放(放大/缩小/重置)1.4.4低视力、老年人
高对比度模式(深色/浅色/反色)1.4.3低视力
深色模式1.4.3光敏感人群
灰度显示1.4.1色盲
色彩饱和度滑块1.4.1色彩敏感者
文字间距调整1.4.12阅读障碍、低视力
阅读障碍专用字体(OpenDyslexic)最佳实践阅读障碍
隐藏图片最佳实践认知障碍
色盲模拟滤镜1.4.1开发者测试、无障碍意识教育

🧭 导航与阅读辅助

功能WCAG 参考适用人群
全键盘导航支持2.1.1运动障碍
焦点指示器增强2.4.7运动障碍、低视力
标题地图 / 页面目录2.4.10屏幕阅读器用户
ARIA 地标结构展示2.4.1屏幕阅读器用户
阅读辅助标尺(遮光引导线)最佳实践阅读障碍、多动症
行聚焦 / 内容遮罩最佳实践多动症、认知障碍
大光标模式最佳实践运动障碍、低视力
屏幕阅读器预览1.1.1, 1.3.1开发者、测试人员

🔊 媒体与内容辅助

功能WCAG 参考适用人群
悬停朗读(TTS)最佳实践视障、低视力
选中朗读(TTS)最佳实践视障、低视力
整页朗读(TTS)最佳实践视障、低视力
链接高亮显示1.4.1, 2.4.4低视力、认知障碍
图片替代文本覆盖显示1.1.1视障、低视力
停止所有动画2.3.1, 2.3.3癫痫、前庭障碍
静音所有声音1.4.2听障、认知障碍

⚙️ 配置参数

您可以通过全局对象 window.OpenA11yConfig 在脚本加载前进行配置。

html
<script>
  window.OpenA11yConfig = {
    position: "bottom-right", // 按钮位置
    lang: "zh", // 界面语言
    bottomOffset: 20, // 桌面端向上偏移(px)
    mobileBottomOffset: 60, // 移动端向上偏移(px)
    statementUrl: "/accessibility-statement", // 外部无障碍声明链接
    statementData: {
      // 内置声明生成器数据
      orgName: "我的公司",
      orgPhone: "13088888888",
      orgEmail: "access@example.com",
      coordinatorName: "张三",
      lastAuditDate: "2026-01-15",
    },
  };
</script>
<script src="/path/to/c-a11y.min.js" defer></script>

配置项说明

参数类型默认值说明
positionstring'bottom-left'可选:bottom-leftbottom-righttop-lefttop-right
langstring自动检测界面语言:'zh''en'
bottomOffsetnumber0桌面端(屏幕 > 768px)按钮额外向上偏移,仅对底部位置生效
mobileBottomOffsetnumber0移动端(屏幕 ≤ 768px)额外向上偏移,适用于底部固定导航栏的页面
statementUrlstring若您已有无障碍声明页面,可在此填写完整 URL
statementDataobject若需内置声明生成器,提供机构信息即可自动生成声明

🌐 多语言支持

插件界面内置 中文(zh)英语(en) 两种语言,用户可随时在工具栏中切换。

📜 合规性说明

本插件不仅覆盖 WCAG 2.1 的常用标准,还额外支持以下成功准则:

  • 1.4.10 回流(Reflow)
  • 1.4.12 文字间距(Text Spacing)
  • 1.4.13 悬停或焦点时的内容(Content on Hover or Focus)
  • 2.3.3 交互动画(Animation from Interactions)
  • 4.1.3 状态消息(Status Messages)

🧱 项目架构

目录结构

src/
  c-a11y.js            # 主入口
  styles.js            # 所有 CSS(注入 Shadow DOM)
  i18n.js              # 中/英翻译
  storage.js           # localStorage 持久化
  utils.js             # 共用工具函数
  modules/             # 每个功能独立模块(共 24 个)
dist/
  c-a11y.min.js        # 压缩版 IIFE(约 89 KB)
  c-a11y.esm.js        # ES Module 版本(供打包工具使用)
demo.html              # 功能演示页面

设计特点

  • Shadow DOM — 样式隔离,完全避免与宿主页面 CSS 冲突。
  • 零依赖 — 纯原生 JavaScript,无需加载任何第三方库。
  • 模块化 — 每个功能提供 enable() / disable() / toggle() 方法,便于扩展。
  • 状态持久化 — 用户偏好自动保存至 localStorage,跨会话保留。
  • ARIA 实时区域 — 为屏幕阅读器提供状态通知(WCAG 4.1.3)。

❓ 常见问题

悬停朗读/选中朗读无法正确朗读中文

问题描述

当页面 HTML 设置了 <html lang="en"> 时,即使插件界面切换到中文,悬停朗读和选中朗读功能仍会使用英文语音朗读中文内容,导致发音异常或无法理解。

原因分析

TTS(文本转语音)模块的语言检测逻辑优先使用页面 HTML 的 lang 属性:

javascript
_getLang() {
  const pageLang = document.documentElement.lang;  // 优先读取 <html lang="...">
  const raw = pageLang || getLanguage() || 'en';   // 其次才是插件界面语言
  return LANG_MAP[raw] || raw;
}

这样设计是为了让 TTS 遵循页面的内容语言,而非插件的界面语言。例如,一个英文网站(<html lang="en">)上使用中文界面的用户,朗读时仍应使用英文语音。

解决方案

1. 推荐做法:在页面 HTML 中正确设置语言属性

html
<!-- 中文网站 -->
<html lang="zh">
  <!-- 或更精确 -->
  <html lang="zh-CN"></html>
</html>

2. 临时方案:如果页面语言与内容语言不一致,可以在控制台临时修改:

javascript
document.documentElement.lang = "zh-CN";

3. 检查系统语音:确保操作系统已安装中文语音包

javascript
// 在浏览器控制台运行,查看是否有中文语音
speechSynthesis.getVoices().filter((v) => v.lang.includes("zh"));
  • macOS:自带中文语音(Ting-Ting)
  • Windows:需在"设置 → 时间和语言 → 语言"中添加中文语言包
  • Linux:通常需要额外安装 espeak 或 festival 等语音合成引擎

Webpack3 引入 ES Module 依赖包编译报 Unexpected token 语法错误

问题描述

项目基于 Webpack3 构建,引入 @william-xu-web/c-a11y 依赖后启动编译报错,出现 Unexpected token 错误,根源为该依赖打包产物为 ES Module 格式(含 import/export 语法),Webpack3 编译时无法识别解析 ESM 语法,构建流程直接中断。

原因分析

  1. Webpack3 仅原生支持 CommonJS 模块化语法,不兼容 ES Module (import/export) 语法,无内置 ESM 解析能力。
  2. 目标依赖 @william-xu-web/c-a11y/dist/c-a11y.esm.js 采用 ESM 规范打包,存在 Webpack3 无法识别的模块化语法。
  3. Webpack3 无 type: "javascript/auto" 兼容配置,无法使用高版本 Webpack 适配 ESM 的配置写法。
  4. 项目默认 babel-loader 全局排除所有 node_modules 文件,未对该特殊依赖单独放行转译,导致 ESM 语法未被编译转换。
解决方案

1. 调整 babel-loader 排除规则(核心操作)

修改 Webpack3 构建配置,无需降级依赖包版本,适配性最强。

找到项目 webpack.base.conf.js 中的 babel-loader 配置,修改排除规则,放行报错依赖包参与 babel 转译:

js
// 原配置
{
  test: /\.js$/,
  loader: 'babel-loader',
  exclude: /node_modules/
}

// 修改后最终配置
{
  test: /\.js$/,
  loader: 'babel-loader',
  exclude: file => {
    // 仅放行 @william-xu-web-c-a11y 依赖,其余 node_modules 正常排除
    if (file.includes('@william-xu-web/c-a11y')) return false
    return /node_modules/.test(file)
  }
}

2. 配置 Babel 强制转换模块化规范

在项目根目录 .babelrc 中添加配置,强制将 ESM 转为 Webpack3 识别的 CommonJS:

json
{
  "presets": [
    [
      "env",
      {
        "modules": "commonjs"
      }
    ]
  ]
}

补充注意事项

  1. Webpack3 禁止使用 rule.type 相关配置,该配置仅 Webpack4 及以上版本支持。
  2. Vue-cli2 等基于 Webpack3 搭建的老旧 Vue 项目,统一修改 build/webpack.base.conf.js 配置即可。

如何根据条件动态引入启用插件

使用场景

在某些项目中,可能需要根据特定条件动态启用无障碍插件,例如:

  • 仅在特定页面或路由启用
  • 根据用户权限或配置动态加载
  • 在生产环境启用,开发环境禁用
  • 根据用户偏好设置延迟加载

核心方法

使用 require() 动态引入实现条件加载,核心模式:

javascript
// 防止重复引入
if (!window.a11yLoaded) {
  require("@william-xu-web/c-a11y");
  window.a11yLoaded = true;
}

// 如需传递配置,在引入前设置
window.OpenA11yConfig = {
  position: "bottom-left",
  lang: "zh",
};
require("@william-xu-web/c-a11y");
典型应用场景
javascript
// 根据环境变量
if (process.env.NODE_ENV === "production") {
  require("@william-xu-web/c-a11y");
}
javascript
// 延迟加载(减少首屏加载时间)
window.addEventListener("load", () => {
  setTimeout(() => {
    if (localStorage.getItem("enable-a11y") === "true") {
      require("@william-xu-web/c-a11y");
    }
  }, 3000);
});
javascript
// Vue 路由守卫
router.afterEach((to) => {
  const a11yRoutes = ["/home", "/about"];
  if (a11yRoutes.includes(to.path) && !window.a11yLoaded) {
    require("@william-xu-web/c-a11y");
    window.a11yLoaded = true;
  }
});
javascript
// React 条件渲染
useEffect(() => {
  if (enableA11y && !window.a11yLoaded) {
    require("@william-xu-web/c-a11y");
    window.a11yLoaded = true;
  }
}, [enableA11y]);

注意事项

  1. 防止重复引入:使用全局变量 window.a11yLoaded 标记加载状态
  2. Webpack 打包:Webpack 会静态分析 require() 并打包依赖,即使条件为 false 也会包含在产物中。如需完全排除,使用 import() 动态导入或在 externals 中排除
  3. 性能优化:推荐在页面加载完成后或用户首次交互时引入,减少首屏 JS 体积

Webpack 环境下 require() 加载的是哪个版本?

问题描述

使用 require("@william-xu-web/c-a11y") 引入时,Webpack 实际加载的是 ESM 版本(c-a11y.esm.js),而非 CommonJS 版本。

原因分析

Webpack 解析优先级:module 字段 > main 字段

json
// c-a11y 的 package.json
{
  "main": "dist/c-a11y.min.js", // CommonJS 入口
  "module": "dist/c-a11y.esm.js" // ESM 入口(Webpack 优先)
}

Webpack 优先选择 ESM 版本,因为支持 Tree-shaking(删除未使用代码),打包体积更小。

如何强制使用 CommonJS 版本

javascript
// 直接指定路径
require("@william-xu-web/c-a11y/dist/c-a11y.min.js");

建议

现代浏览器推荐使用 ESM 版本,特殊场景下可能需要使用 CommonJS 版本。

IE 11 中条件 require() 仍然报错

问题描述

即使在 IE 11 中使用条件判断 if (false) { require(...) },代码不会执行,但 IE 仍然会报语法错误。

原因分析

JavaScript 有两个阶段:

阶段IE 行为
解析阶段扫描整个文件,检查语法 ❌ 发现 ES6 语法(模板字符串等)→ 报错
执行阶段因解析失败,根本不会执行
解决方案

方案 1:配置 Babel 转译第三方包

javascript
// vue.config.js
module.exports = {
  transpileDependencies: ["@william-xu-web/c-a11y"],
};

// 或 babel.config.js
module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"],
  overrides: [
    {
      test: /node_modules[\\/]@william-xu-web[\\/]c-a11y/,
      presets: [["@babel/preset-env", { targets: { ie: "11" } }]],
    },
  ],
};

方案 2:使用动态 import()

javascript
// Webpack 会单独打包,按需加载
async function loadA11y() {
  if (shouldLoadA11y()) {
    await import("@william-xu-web/c-a11y");
  }
}

方案 3:添加 polyfill

javascript
// main.js 顶部
import "core-js/stable";
import "regenerator-runtime/runtime";

🌍 浏览器支持

浏览器最低版本状态
Chrome80+推荐
Firefox78+支持
Safari14+支持
Edge80+支持
IE任何版本不兼容

🔗 相关链接