Accessibility Auditor by davila7/claude-code-templates
npx skills add https://github.com/davila7/claude-code-templates --skill 'Accessibility Auditor'为创建符合 WCAG 标准、有效服务所有能力用户的无障碍网络体验提供全面指导。
在以下情况使用此技能:
用户必须能够感知所呈现的信息。
用户必须能够操作界面。
用户必须能够理解信息和界面。
内容必须足够健壮,以便与当前和未来的技术协同工作。
❌ 问题:
<img src="/products/shoes.jpg">
✅ 解决方案:
<!-- 信息性图像 -->
<img src="/products/shoes.jpg" alt="带有白色 Swoosh 标志的红色 Nike Air Max 跑鞋">
<!-- 装饰性图像 -->
<img src="/decorative-pattern.svg" alt="" role="presentation">
<!-- 链接的 Logo -->
<a href="/">
<img src="/logo.png" alt="公司名称 - 首页">
</a>
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
规则:
alt="")❌ 问题:
/* 对比度 2.5:1 - 不符合 WCAG */
.text {
color: #767676;
background: #ffffff;
}
✅ 解决方案:
/* 对比度 4.5:1+ - 符合 AA 级 */
.text {
color: #595959;
background: #ffffff;
}
/* 对比度 7:1+ - 符合 AAA 级 */
.text-high-contrast {
color: #333333;
background: #ffffff;
}
要求:
❌ 问题:
<div class="button" onclick="submitForm()">提交</div>
<div class="heading">页面标题</div>
<div class="nav-menu">...</div>
✅ 解决方案:
<button type="submit" onclick="submitForm()">提交</button>
<h1>页面标题</h1>
<nav aria-label="主导航">...</nav>
语义化元素:
<button> 用于按钮<a> 用于链接<h1> - <h6> 用于标题 (层级结构)<nav>, <main>, <aside>, <article>, <section> 用于地标<ul>, <ol>, <li> 用于列表<table>, <th>, <td> 用于表格数据❌ 问题:
<input type="email" placeholder="输入您的邮箱">
✅ 解决方案:
<!-- 显式标签 -->
<label for="email">邮箱地址</label>
<input type="email" id="email" name="email">
<!-- 隐式标签 -->
<label>
邮箱地址
<input type="email" name="email">
</label>
<!-- 隐藏标签 (用于紧凑布局) -->
<label for="search" class="sr-only">搜索</label>
<input type="text" id="search" placeholder="搜索...">
最佳实践:
<fieldset> 和 <legend> 对相关字段进行分组❌ 问题:
<div onclick="handleClick()">点击我</div>
<a href="javascript:void(0)" onclick="doSomething()">操作</a>
✅ 解决方案:
<!-- 使用正确的按钮 -->
<button onclick="handleClick()">点击我</button>
<!-- 如果必须使用 div,请使其可访问 -->
<div
role="button"
tabindex="0"
onclick="handleClick()"
onkeydown="handleKeyPress(event)"
>
点击我
</div>
<script>
function handleKeyPress(event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleClick();
}
}
</script>
键盘要求:
❌ 问题:
<div class="header">...</div>
<div class="main-content">...</div>
<div class="sidebar">...</div>
<div class="footer">...</div>
✅ 解决方案:
<header role="banner">
<nav aria-label="主导航">...</nav>
</header>
<main role="main">
<h1>页面标题</h1>
<article>...</article>
</main>
<aside role="complementary" aria-label="相关文章">
...
</aside>
<footer role="contentinfo">
...
</footer>
常见地标:
banner - 网站页眉navigation - 导航菜单main - 主要内容 (每页一个)complementary - 辅助内容contentinfo - 网站页脚search - 搜索功能form - 表单区域❌ 问题:
<div class="modal">
<div class="content">
模态框内容
<button onclick="closeModal()">关闭</button>
</div>
</div>
✅ 解决方案:
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-desc"
>
<h2 id="modal-title">确认操作</h2>
<p id="modal-desc">您确定要删除此项吗?</p>
<button onclick="confirmAction()">确认</button>
<button onclick="closeModal()">取消</button>
</div>
<script>
// 焦点管理
function openModal() {
const modal = document.querySelector('[role="dialog"]');
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
// 存储之前的焦点
previousFocus = document.activeElement;
// 聚焦第一个元素
focusableElements[0].focus();
// 焦点陷阱
modal.addEventListener('keydown', trapFocus);
}
function closeModal() {
// 恢复焦点
if (previousFocus) previousFocus.focus();
}
function trapFocus(event) {
if (event.key !== 'Tab') return;
const focusableElements = Array.from(
modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (event.shiftKey && document.activeElement === firstElement) {
lastElement.focus();
event.preventDefault();
} else if (!event.shiftKey && document.activeElement === lastElement) {
firstElement.focus();
event.preventDefault();
}
}
</script>
模态框要求:
role="dialog" 或 role="alertdialog"aria-modal="true" 以指示模态行为aria-labelledby 指向标题aria-describedby 用于描述 (可选)✅ 解决方案:
<a href="#main-content" class="skip-link">
跳转到主要内容
</a>
<header>
<nav>...</nav>
</header>
<main id="main-content" tabindex="-1">
<!-- 页面内容 -->
</main>
<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
</style>
状态:
aria-checked - 复选框/单选按钮状态aria-disabled - 禁用状态aria-expanded - 展开/折叠状态aria-hidden - 对辅助技术隐藏aria-pressed - 切换按钮状态aria-selected - 选中状态属性:
aria-label - 无障碍名称aria-labelledby - 标签的 ID 引用aria-describedby - 描述的 ID 引用aria-live - 实时区域更新aria-required - 必填字段aria-invalid - 验证状态<!-- Polite: 等待语音暂停 -->
<div aria-live="polite" aria-atomic="true">
商品已加入购物车
</div>
<!-- Assertive: 立即中断 -->
<div aria-live="assertive" role="alert">
错误:支付失败
</div>
<!-- 状态消息 -->
<div role="status" aria-live="polite">
正在保存更改...
</div>
手风琴:
<div class="accordion">
<button
aria-expanded="false"
aria-controls="panel-1"
id="accordion-1"
>
第 1 部分
</button>
<div id="panel-1" role="region" aria-labelledby="accordion-1" hidden>
面板内容
</div>
</div>
标签页:
<div role="tablist" aria-label="内容部分">
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
id="tab-1"
>
标签页 1
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-2"
id="tab-2"
tabindex="-1"
>
标签页 2
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
面板 1 内容
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
面板 2 内容
</div>
VoiceOver (Mac):
NVDA (Windows):
测试场景:
在网站上包含:
# 无障碍声明
我们致力于确保残障人士的数字无障碍访问。我们持续改进所有人的用户体验,并应用相关的无障碍标准。
## 符合性状态
本网站部分符合 WCAG 2.1 AA 级标准。"部分符合"意味着内容的某些部分未完全符合无障碍标准。
## 反馈
我们欢迎您对本网站无障碍性的反馈。请联系我们:
- 邮箱:accessibility@example.com
- 电话:+1-555-0123
## 已知问题
- [列出任何已知的无障碍问题及计划修复]
最后更新:[日期]
工具:
指南:
无障碍不是可选项——它是创建包容性网络体验的基本要求。在每个项目开始时就要优先考虑它,而不是事后补救。
每周安装数
0
仓库
GitHub 星标数
22.6K
首次出现
Jan 1, 1970
安全审计
Comprehensive guidance for creating accessible web experiences that comply with WCAG standards and serve users of all abilities effectively.
Use this skill when:
Users must be able to perceive the information being presented.
Users must be able to operate the interface.
Users must be able to understand the information and interface.
Content must be robust enough to work with current and future technologies.
❌ Problem:
<img src="/products/shoes.jpg">
✅ Solution:
<!-- Informative image -->
<img src="/products/shoes.jpg" alt="Red Nike Air Max running shoes with white swoosh">
<!-- Decorative image -->
<img src="/decorative-pattern.svg" alt="" role="presentation">
<!-- Logo that links -->
<a href="/">
<img src="/logo.png" alt="Company Name - Home">
</a>
Rules:
❌ Problem:
/* Contrast ratio 2.5:1 - Fails WCAG */
.text {
color: #767676;
background: #ffffff;
}
✅ Solution:
/* Contrast ratio 4.5:1+ - Passes AA */
.text {
color: #595959;
background: #ffffff;
}
/* Contrast ratio 7:1+ - Passes AAA */
.text-high-contrast {
color: #333333;
background: #ffffff;
}
Requirements:
❌ Problem:
<div class="button" onclick="submitForm()">Submit</div>
<div class="heading">Page Title</div>
<div class="nav-menu">...</div>
✅ Solution:
<button type="submit" onclick="submitForm()">Submit</button>
<h1>Page Title</h1>
<nav aria-label="Main navigation">...</nav>
Semantic Elements:
<button> for buttons<a> for links<h1> - <h6> for headings (hierarchical)<nav>, <main>, <aside>, <article>, <section> for landmarks<ul>, <ol>, for lists❌ Problem:
<input type="email" placeholder="Enter your email">
✅ Solution:
<!-- Explicit label -->
<label for="email">Email Address</label>
<input type="email" id="email" name="email">
<!-- Implicit label -->
<label>
Email Address
<input type="email" name="email">
</label>
<!-- Hidden label (for tight layouts) -->
<label for="search" class="sr-only">Search</label>
<input type="text" id="search" placeholder="Search...">
Best Practices:
<fieldset> and <legend>❌ Problem:
<div onclick="handleClick()">Click me</div>
<a href="javascript:void(0)" onclick="doSomething()">Action</a>
✅ Solution:
<!-- Use proper button -->
<button onclick="handleClick()">Click me</button>
<!-- If div required, make it accessible -->
<div
role="button"
tabindex="0"
onclick="handleClick()"
onkeydown="handleKeyPress(event)"
>
Click me
</div>
<script>
function handleKeyPress(event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleClick();
}
}
</script>
Keyboard Requirements:
❌ Problem:
<div class="header">...</div>
<div class="main-content">...</div>
<div class="sidebar">...</div>
<div class="footer">...</div>
✅ Solution:
<header role="banner">
<nav aria-label="Main navigation">...</nav>
</header>
<main role="main">
<h1>Page Title</h1>
<article>...</article>
</main>
<aside role="complementary" aria-label="Related articles">
...
</aside>
<footer role="contentinfo">
...
</footer>
Common Landmarks:
banner - Site headernavigation - Navigation menusmain - Primary content (one per page)complementary - Supporting contentcontentinfo - Site footersearch - Search functionalityform - Form regions❌ Problem:
<div class="modal">
<div class="content">
Modal content
<button onclick="closeModal()">Close</button>
</div>
</div>
✅ Solution:
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-desc"
>
<h2 id="modal-title">Confirm Action</h2>
<p id="modal-desc">Are you sure you want to delete this item?</p>
<button onclick="confirmAction()">Confirm</button>
<button onclick="closeModal()">Cancel</button>
</div>
<script>
// Focus management
function openModal() {
const modal = document.querySelector('[role="dialog"]');
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
// Store previous focus
previousFocus = document.activeElement;
// Focus first element
focusableElements[0].focus();
// Trap focus
modal.addEventListener('keydown', trapFocus);
}
function closeModal() {
// Return focus
if (previousFocus) previousFocus.focus();
}
function trapFocus(event) {
if (event.key !== 'Tab') return;
const focusableElements = Array.from(
modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (event.shiftKey && document.activeElement === firstElement) {
lastElement.focus();
event.preventDefault();
} else if (!event.shiftKey && document.activeElement === lastElement) {
firstElement.focus();
event.preventDefault();
}
}
</script>
Modal Requirements:
role="dialog" or role="alertdialog"aria-modal="true" to indicate modal behavioraria-labelledby pointing to titlearia-describedby for description (optional)✅ Solution:
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<header>
<nav>...</nav>
</header>
<main id="main-content" tabindex="-1">
<!-- Page content -->
</main>
<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
</style>
States:
aria-checked - Checkbox/radio statearia-disabled - Disabled statearia-expanded - Expanded/collapsed statearia-hidden - Hidden from assistive technologyaria-pressed - Toggle button statearia-selected - Selected stateProperties:
aria-label - Accessible namearia-labelledby - ID reference for labelaria-describedby - ID reference for descriptionaria-live - Live region updatesaria-required - Required fieldaria-invalid - Validation state<!-- Polite: Wait for pause in speech -->
<div aria-live="polite" aria-atomic="true">
Item added to cart
</div>
<!-- Assertive: Interrupt immediately -->
<div aria-live="assertive" role="alert">
Error: Payment failed
</div>
<!-- Status message -->
<div role="status" aria-live="polite">
Saving changes...
</div>
Accordion:
<div class="accordion">
<button
aria-expanded="false"
aria-controls="panel-1"
id="accordion-1"
>
Section 1
</button>
<div id="panel-1" role="region" aria-labelledby="accordion-1" hidden>
Panel content
</div>
</div>
Tabs:
<div role="tablist" aria-label="Content sections">
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
id="tab-1"
>
Tab 1
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-2"
id="tab-2"
tabindex="-1"
>
Tab 2
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
Panel 1 content
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
Panel 2 content
</div>
VoiceOver (Mac):
NVDA (Windows):
Test Scenarios:
Include on website:
# Accessibility Statement
We are committed to ensuring digital accessibility for people with disabilities. We continually improve the user experience for everyone and apply relevant accessibility standards.
## Conformance Status
This website is partially conformant with WCAG 2.1 Level AA. "Partially conformant" means that some parts of the content do not fully conform to the accessibility standard.
## Feedback
We welcome your feedback on the accessibility of this site. Please contact us:
- Email: accessibility@example.com
- Phone: +1-555-0123
## Known Issues
- [List any known accessibility issues and planned fixes]
Last updated: [Date]
Tools:
Guidelines:
Accessibility is not optional—it's a fundamental requirement for creating inclusive web experiences. Prioritize it from the start of every project, not as an afterthought.
Weekly Installs
0
Repository
GitHub Stars
22.6K
First Seen
Jan 1, 1970
Security Audits
测试策略完整指南:单元/集成/E2E测试金字塔与自动化实践
11,200 周安装
PDF转Markdown工具:自动检测原生/扫描文档,支持OCR转换
427 周安装
Spring Boot CRUD模式指南:DDD分层架构与REST API实现
425 周安装
Biome:比ESLint快100倍的一体化代码检查和格式化工具链 | Rust编写
424 周安装
小红书运营自动化技能:账号定位、爆款选题、内容生成与发布全流程SOP指南
424 周安装
Express REST API 开发教程:5步构建健壮可扩展的Node.js后端服务
424 周安装
Firebase Authentication 身份验证入门教程 - 用户管理与安全配置指南
432 周安装
<li><table>, <th>, <td> for tabular data