deer-sense
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDeer Sense 🦌
鹿之感知 🦌
The deer moves through the forest with heightened awareness. It hears what others miss—a twig snapping underfoot, a bird's warning call. It notices what blocks the path. The deer guides the herd around danger, ensuring everyone can travel safely. In the digital forest, the deer senses barriers that stop some wanderers from finding their way.
鹿带着高度的警觉穿行于森林之中。它能听见他人错过的声音——脚下断裂的细枝、鸟儿的警示鸣叫。它会留意阻挡道路的事物,引导鹿群避开危险,确保每一个成员都能安全前行。在数字森林里,鹿能感知到那些阻碍部分探索者前行的障碍。
When to Activate
激活场景
- User asks to "check accessibility" or "a11y audit"
- User says "make this accessible" or "screen reader test"
- User calls or mentions deer/accessibility
/deer-sense - Building new UI components
- Reviewing existing interfaces
- After visual redesigns
- Before major releases
- When accessibility complaints arise
Pair with: for accessible UI implementation
chameleon-adapt- 用户要求「检查无障碍访问」或「a11y审计」
- 用户提出「优化无障碍访问」或「屏幕阅读器测试」
- 用户调用 或提及鹿/无障碍访问
/deer-sense - 构建新UI组件时
- 评审现有界面时
- 视觉重设计完成后
- 重大版本发布前
- 收到无障碍访问相关投诉时
搭配使用: 用于无障碍UI落地
chameleon-adaptThe Sense
感知流程
LISTEN → SCAN → TEST → GUIDE → PROTECT
↓ ↲ ↲ ↓ ↓
Hear Look for Validate Teach Ensure
Needs Barriers Paths Good InclusionLISTEN → SCAN → TEST → GUIDE → PROTECT
↓ ↲ ↲ ↓ ↓
聆听需求 排查障碍 验证路径 引导优化 保障包容性Phase 1: LISTEN
阶段1:LISTEN(聆听)
The deer's ears twitch, hearing what others miss...
Understand accessibility needs:
Who Are We Building For?
| Disability Type | Assistive Technology | What They Need |
|---|---|---|
| Visual | Screen readers, magnification | Alt text, semantic HTML, focus indicators |
| Motor | Keyboard, switch controls | Keyboard navigation, large touch targets |
| Cognitive | Simplified interfaces | Clear language, consistent patterns |
| Auditory | Captions, visual indicators | Transcripts, visual alerts |
WCAG Levels:
- A — Essential (must have)
- AA — Ideal (Grove standard)
- AAA — Enhanced (when possible)
Grove Standard: WCAG 2.1 AA
Common Barriers:
Visual Barriers:
- Poor color contrast
- Missing alt text
- Keyboard traps
- No focus indicators
Motor Barriers:
- Small touch targets (<44px)
- No keyboard support
- Time limits too short
- Required precision
Cognitive Barriers:
- Confusing navigation
- Missing error explanations
- Inconsistent patterns
- Too much information at onceOutput: Accessibility requirements defined for this project
鹿耳微动,捕捉他人遗漏的声音...
理解无障碍访问需求:
我们为谁构建产品?
| 障碍类型 | 辅助技术 | 需求要点 |
|---|---|---|
| 视觉障碍 | 屏幕阅读器、放大镜 | 替代文本(Alt text)、语义化HTML、焦点指示器 |
| 肢体障碍 | 键盘、切换控制设备 | 键盘导航、大尺寸触控目标 |
| 认知障碍 | 简化界面 | 清晰语言、一致交互模式 |
| 听觉障碍 | 字幕、视觉指示器 | 文字转录、视觉提醒 |
WCAG 等级:
- A — 基础必备(必须满足)
- AA — 理想标准(Grove 规范)
- AAA — 增强级(尽可能满足)
Grove 标准:WCAG 2.1 AA
常见障碍:
视觉障碍:
- 色彩对比度不足
- 缺少替代文本
- 键盘陷阱(无法Tab离开)
- 无焦点指示器
肢体障碍:
- 触控目标过小(<44px)
- 无键盘支持
- 时间限制过短
- 操作精度要求过高
认知障碍:
- 导航逻辑混乱
- 错误提示不清晰
- 交互模式不一致
- 信息过载输出: 项目专属的无障碍访问需求定义
Phase 2: SCAN
阶段2:SCAN(扫描)
The deer's eyes scan the forest floor, spotting what blocks the path...
Automated accessibility scanning:
axe-core (Recommended):
bash
undefined鹿眼扫视林间地面,发现阻挡道路的事物...
自动化无障碍访问扫描:
axe-core(推荐):
bash
undefinedInstall
安装
npm install --save-dev @axe-core/cli
npm install --save-dev @axe-core/cli
Run on your site
扫描站点
npx axe https://localhost:5173 --tags wcag2aa
npx axe https://localhost:5173 --tags wcag2aa
Or in tests
集成到测试中
npm install --save-dev @axe-core/puppeteer
**Lighthouse CI:**
```bashnpm install --save-dev @axe-core/puppeteer
**Lighthouse CI:**
```bashIn CI pipeline
在CI流水线中执行
Look for scores and specific failures
查看评分和具体问题
**Svelte Accessibility Warnings:**
```svelte
<!-- Svelte warns about common issues -->
<!-- ❌ Missing alt text -->
<img src="photo.jpg">
<!-- ✅ Descriptive alt -->
<img src="photo.jpg" alt="Sunset over the grove">
<!-- ❌ No label -->
<input type="text">
<!-- ✅ Associated label -->
<label for="email">Email</label>
<input id="email" type="text">
<!-- ❌ Click without keyboard -->
<div onclick={handleClick}>
<!-- ✅ Button with keyboard -->
<button onclick={handleClick}>Common Issues to Check:
typescript
// a11y-checklist.ts
const checks = {
// Images
imagesHaveAlt: 'All <img> have descriptive alt text',
decorativeMarked: 'Decorative images have alt=""',
// Forms
labelsPresent: 'All inputs have associated labels',
errorsClear: 'Error messages explain how to fix',
requiredMarked: 'Required fields are indicated',
// Navigation
focusVisible: 'Focus indicators are visible',
skipLinks: 'Skip navigation links present',
headingOrder: 'Headings follow logical order (h1→h2→h3)',
// Color
contrastAA: 'Text contrast ratio >= 4.5:1',
contrastLarge: 'Large text contrast >= 3:1',
notColorOnly: 'Information not conveyed by color alone',
// Structure
landmarks: 'Page has main, nav, complementary landmarks',
langAttribute: '<html lang="en"> set correctly',
titlePresent: 'Each page has unique <title>'
};Output: Automated scan results with specific violations
**Svelte 无障碍警告:**
```svelte
<!-- Svelte 会警告常见问题 -->
<!-- ❌ 缺少替代文本 -->
<img src="photo.jpg">
<!-- ✅ 描述性替代文本 -->
<img src="photo.jpg" alt="林间日落">
<!-- ❌ 无标签 -->
<input type="text">
<!-- ✅ 关联标签 -->
<label for="email">邮箱</label>
<input id="email" type="text">
<!-- ❌ 仅支持点击的元素无键盘交互 -->
<div onclick={handleClick}>
<!-- ✅ 支持键盘交互的按钮 -->
<button onclick={handleClick}>需检查的常见问题:
typescript
// a11y-checklist.ts
const checks = {
// 图片
imagesHaveAlt: '所有<img>标签包含描述性替代文本',
decorativeMarked: '装饰性图片设置alt=""',
// 表单
labelsPresent: '所有输入框关联对应标签',
errorsClear: '错误提示说明修复方法',
requiredMarked: '必填字段有明确标识',
// 导航
focusVisible: '焦点指示器可见',
skipLinks: '提供跳过导航的快捷链接',
headingOrder: '标题遵循逻辑层级(h1→h2→h3)',
// 色彩
contrastAA: '文本对比度≥4.5:1',
contrastLarge: '大文本对比度≥3:1',
notColorOnly: '信息不单独通过色彩传递',
// 结构
landmarks: '页面包含main、nav、complementary等地标元素',
langAttribute: '<html lang="en">设置正确',
titlePresent: '每个页面有唯一的<title>'
};输出: 包含具体违规项的自动化扫描结果
Phase 3: TEST
阶段3:TEST(测试)
The deer tests each path, ensuring it can be traveled...
Manual accessibility testing:
Keyboard Navigation:
Test Checklist:
□ Tab through entire page - does order make sense?
□ Can you activate every button/link with Enter/Space?
□ Is there a visible focus indicator on everything?
□ Can you escape modals with Escape key?
□ No keyboard traps (can't Tab away)?
□ Skip link works (jumps past navigation)?Screen Reader Testing:
bash
undefined鹿会测试每条路径,确保所有成员都能通行...
手动无障碍访问测试:
键盘导航测试:
测试清单:
□ 按Tab键遍历整个页面 - 顺序是否符合逻辑?
□ 能否用Enter/Space激活所有按钮/链接?
□ 所有可交互元素的焦点指示器是否可见?
□ 能否用Escape键关闭弹窗?
□ 无键盘陷阱(无法Tab离开)?
□ 跳过导航的快捷链接是否可用?屏幕阅读器测试:
bash
undefinedNVDA (Windows) - Free
NVDA (Windows) - 免费
JAWS (Windows) - Industry standard
JAWS (Windows) - 行业标准
VoiceOver (macOS) - Built-in
VoiceOver (macOS) - 内置
TalkBack (Android) - Built-in
TalkBack (Android) - 内置
**Screen Reader Checklist:**
□ Page title announced on load
□ Headings navigate correctly (H key)
□ Landmarks listed (D key)
□ Images have descriptive text
□ Buttons say what they do
□ Form labels read correctly
□ Error messages announced
□ Status updates announced (aria-live)
**Reduced Motion:**
```svelte
<script>
import { reducedMotion } from '$lib/stores/accessibility';
</script>
{#if !$reducedMotion}
<FallingLeavesLayer />
{:else}
<!-- Static version for reduced motion -->
<StaticLeaves />
{/if}Zoom Testing:
Test at:
□ 100% (normal)
□ 150% (mild vision loss)
□ 200% (moderate vision loss)
□ 400% (severe vision loss)
Verify:
- No horizontal scrolling at 200%
- All content still visible
- No overlapping elementsExample Walkthrough Scenarios:
Scenario 1: Testing a Form with Conditional Fields
Setup: Multi-step registration form with conditional fields
1. Tab to email field
✓ Focus indicator visible?
✓ Label announced by screen reader?
2. Enter invalid email, Tab away
✓ Error message announced live?
✓ Error associated with field (aria-describedby)?
3. Check "Business account" checkbox
✓ New company name field appears
✓ Focus moves to new field OR stays logical?
✓ Screen reader announces new content?
4. Tab through to submit
✓ Skip link available to jump sections?
✓ Submit button clearly labeled?
✓ Can activate with Enter AND Space?Scenario 2: Testing GroveTerm Components
Setup: Page using GroveTerm/GroveSwap/GroveText for terminology
1. Tab to a GroveTerm element
✓ Focus ring visible (2px outline)?
✓ aria-label reads "Grove term: [term], [category] category"?
✓ role="button" and tabindex="0" present?
2. Activate with Enter or Space
✓ Popup opens with definition?
✓ Focus moves to popup content?
✓ ESC closes popup and returns focus?
3. Toggle Grove Mode OFF
✓ Standard terms display (e.g., "Posts" not "Blooms")?
✓ Screen reader announces "Grove's name for [standard term]" in popup?
✓ GroveSwap silently updates without focus disruption?
4. Check prefers-reduced-motion
✓ No animations on GroveTerm hover/open?
✓ Popup appears instantly without transition?
5. GroveText with [[term]] syntax
✓ Parsed terms are keyboard-navigable?
✓ Each rendered GroveTerm has correct aria attributes?Scenario 3: Testing a Data Table with Sorting
Setup: Posts table with sortable columns
1. Navigate to table
✓ Table has caption or aria-label?
✓ Column headers use <th scope="col">?
✓ Row headers use <th scope="row">?
2. Tab to "Sort by date" column header
✓ Sort button focusable?
✓ Current sort direction announced (aria-sort)?
3. Activate sort with Enter
✓ Table updates without losing focus?
✓ Sort change announced (aria-live)?
✓ New sort direction reflected?
4. Navigate table with arrow keys (screen reader)
✓ Can move cell-by-cell?
✓ Row/column context announced?Scenario 3: Testing a Modal Dialog
Setup: "Delete post" confirmation modal
1. Trigger modal from button
✓ Focus moves INTO modal?
✓ First focusable element receives focus?
✓ Modal has role="dialog" and aria-modal="true"?
✓ Modal has accessible name (aria-labelledby)?
2. Tab through modal
✓ Focus trapped inside modal?
✓ Tab wraps from last to first element?
✓ Shift+Tab works backwards?
3. Press Escape
✓ Modal closes?
✓ Focus returns to trigger button?
4. Click overlay/backdrop
✓ Modal closes (if intended behavior)?
✓ Or click does nothing (if modal is critical)?Output: Manual testing complete with issue log
**屏幕阅读器测试清单:**
□ 页面加载时会播报页面标题
□ 标题导航正常(按H键)
□ 地标元素可被列出(按D键)
□ 图片有描述性文本
□ 按钮功能清晰
□ 表单标签播报正确
□ 错误提示会被播报
□ 状态更新会被实时播报(aria-live)
**减少动画测试:**
```svelte
<script>
import { reducedMotion } from '$lib/stores/accessibility';
</script>
{#if !$reducedMotion}
<FallingLeavesLayer />
{:else}
<!-- 静态版本适配减少动画需求 -->
<StaticLeaves />
{/if}缩放测试:
测试缩放比例:
□ 100%(正常)
□ 150%(轻度视力障碍)
□ 200%(中度视力障碍)
□ 400%(重度视力障碍)
验证内容:
- 200%缩放时无横向滚动
- 所有内容仍可见
- 无元素重叠测试场景示例:
场景1:带条件字段的表单测试
场景:多步骤注册表单,包含条件显示字段
1. Tab切换到邮箱输入框
✓ 焦点指示器可见?
✓ 屏幕阅读器会播报标签?
2. 输入无效邮箱,按Tab离开
✓ 错误提示会实时播报?
✓ 错误提示与输入框关联(aria-describedby)?
3. 勾选「企业账户」复选框
✓ 新的公司名称字段显示?
✓ 焦点移动到新字段或保持逻辑顺序?
✓ 屏幕阅读器播报新增内容?
4. Tab切换到提交按钮
✓ 提供跳过章节的快捷链接?
✓ 提交按钮标签清晰?
✓ 可用Enter和Space键激活?场景2:GroveTerm组件测试
场景:使用GroveTerm/GroveSwap/GroveTerminology的页面
1. Tab切换到GroveTerm元素
✓ 焦点环可见(2px轮廓)?
✓ aria-label显示「Grove术语: [术语名], [分类]」?
✓ 包含role="button"和tabindex="0"属性?
2. 用Enter或Space激活
✓ 弹出定义窗口?
✓ 焦点移动到弹窗内容?
✓ 按ESC关闭弹窗并返回原焦点?
3. 关闭Grove模式
✓ 显示标准术语(如「Posts」而非「Blooms」)?
✓ 屏幕阅读器在弹窗中播报「Grove对[标准术语]的命名」?
✓ GroveSwap静默更新且不干扰焦点?
4. 检查prefers-reduced-motion
✓ GroveTerm hover/打开时无动画?
✓ 弹窗直接显示无过渡效果?
5. 带[[term]]语法的GroveText
✓ 解析后的术语支持键盘导航?
✓ 每个渲染的GroveTerm属性正确?场景3:带排序功能的数据表格测试
场景:可排序的文章列表表格
1. 导航到表格
✓ 表格有标题或aria-label?
✓ 列头使用<th scope="col">?
✓ 行头使用<th scope="row">?
2. Tab切换到「按日期排序」列头
✓ 排序按钮可获取焦点?
✓ 播报当前排序方向(aria-sort)?
3. 按Enter激活排序
✓ 表格更新时不丢失焦点?
✓ 排序变更被实时播报(aria-live)?
✓ 反映新的排序方向?
4. 用箭头键导航表格(屏幕阅读器)
✓ 可逐单元格移动?
✓ 播报行/列上下文?场景4:弹窗对话框测试
场景:「删除文章」确认弹窗
1. 点击按钮触发弹窗
✓ 焦点移动到弹窗内?
✓ 第一个可交互元素获取焦点?
✓ 弹窗设置role="dialog"和aria-modal="true"?
✓ 弹窗有可访问名称(aria-labelledby)?
2. Tab遍历弹窗
✓ 焦点被限制在弹窗内?
✓ Tab从最后一个元素循环到第一个?
✓ Shift+Tab可反向遍历?
3. 按Escape键
✓ 弹窗关闭?
✓ 焦点返回触发按钮?
4. 点击遮罩层
✓ 弹窗关闭(若为预期行为)?
✓ 或点击无响应(若弹窗为关键操作)?输出: 完成手动测试并记录问题
Phase 4: GUIDE
阶段4:GUIDE(引导)
The deer guides the herd around obstacles, showing the clear path...
Fix accessibility issues:
Color Contrast:
svelte
<!-- Check contrast ratios -->
<!-- Use WebAIM contrast checker or similar -->
<!-- Bad: Light gray on white (fails) -->
<p class="text-gray-400">Subtle text</p>
<!-- Good: Darker gray (passes AA) -->
<p class="text-gray-600">Readable text</p>
<!-- Use the palette (already tested for contrast) -->
<p class="text-greens-grove">Brand text</p>Semantic HTML:
svelte
<!-- ❌ Div soup -->
<div class="card" onclick={handleClick}>
<div class="title">Heading</div>
<div class="text">Content</div>
</div>
<!-- ✅ Proper semantics -->
<article class="card">
<h2>Heading</h2>
<p>Content</p>
<button onclick={handleClick}>Action</button>
</article>Focus Management:
svelte
<script>
let modalOpen = $state(false);
let modalRef: HTMLDivElement;
let previousFocus: Element;
function openModal() {
previousFocus = document.activeElement;
modalOpen = true;
// Focus first input when modal opens
tick().then(() => {
modalRef?.querySelector('input')?.focus();
});
}
function closeModal() {
modalOpen = false;
// Return focus to trigger button
previousFocus?.focus();
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') closeModal();
}
</script>
{#if modalOpen}
<div
bind:this={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
onkeydown={handleKeydown}
>
<h2 id="modal-title">Modal Title</h2>
<!-- Content -->
</div>
{/if}ARIA Labels:
svelte
<!-- When visible text isn't enough -->
<button aria-label="Close dialog">
<XIcon />
</button>
<!-- For icon-only buttons -->
<button aria-label="Add to favorites">
<HeartIcon />
</button>
<!-- Current state -->
<button aria-pressed={isExpanded}>
Expand section
</button>
<!-- Live regions for updates -->
<div aria-live="polite" aria-atomic="true">
{statusMessage}
</div>Form Accessibility:
svelte
<form onsubmit={handleSubmit}>
<div>
<label for="email">Email Address *</label>
<input
id="email"
type="email"
required
aria-required="true"
aria-invalid={emailError ? 'true' : 'false'}
aria-describedby={emailError ? 'email-error' : undefined}
bind:value={email}
/>
{#if emailError}
<span id="email-error" role="alert" class="error">
{emailError}
</span>
{/if}
</div>
<button type="submit">Submit</button>
</form>Output: Issues fixed with accessible implementations
鹿引导鹿群绕过障碍,指明清晰路径...
修复无障碍访问问题:
色彩对比度优化:
svelte
<!-- 检查对比度比例 -->
<!-- 使用WebAIM对比度检查工具等 -->
<!-- 不佳:浅灰配白色(不达标) -->
<p class="text-gray-400">次要文本</p>
<!-- 良好:深灰色(符合AA标准) -->
<p class="text-gray-600">可读文本</p>
<!-- 使用经过对比度测试的调色板 -->
<p class="text-greens-grove">品牌文本</p>语义化HTML:
svelte
<!-- ❌ 无意义的Div嵌套 -->
<div class="card" onclick={handleClick}>
<div class="title">标题</div>
<div class="text">内容</div>
</div>
<!-- ✅ 正确的语义化结构 -->
<article class="card">
<h2>标题</h2>
<p>内容</p>
<button onclick={handleClick}>操作</button>
</article>焦点管理:
svelte
<script>
let modalOpen = $state(false);
let modalRef: HTMLDivElement;
let previousFocus: Element;
function openModal() {
previousFocus = document.activeElement;
modalOpen = true;
// 弹窗打开时聚焦第一个输入框
tick().then(() => {
modalRef?.querySelector('input')?.focus();
});
}
function closeModal() {
modalOpen = false;
// 返回焦点到触发按钮
previousFocus?.focus();
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') closeModal();
}
</script>
{#if modalOpen}
<div
bind:this={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
onkeydown={handleKeydown}
>
<h2 id="modal-title">弹窗标题</h2>
<!-- 内容 -->
</div>
{/if}ARIA标签:
svelte
<!-- 当可见文本不足时 -->
<button aria-label="关闭对话框">
<XIcon />
</button>
<!-- 纯图标按钮 -->
<button aria-label="添加到收藏">
<HeartIcon />
</button>
<!-- 当前状态标识 -->
<button aria-pressed={isExpanded}>
展开章节
</button>
<!-- 实时更新区域 -->
<div aria-live="polite" aria-atomic="true">
{statusMessage}
</div>表单无障碍优化:
svelte
<form onsubmit={handleSubmit}>
<div>
<label for="email">电子邮箱 *</label>
<input
id="email"
type="email"
required
aria-required="true"
aria-invalid={emailError ? 'true' : 'false'}
aria-describedby={emailError ? 'email-error' : undefined}
bind:value={email}
/>
{#if emailError}
<span id="email-error" role="alert" class="error">
{emailError}
</span>
{/if}
</div>
<button type="submit">提交</button>
</form>输出: 修复问题并实现无障碍访问
Phase 5: PROTECT
阶段5:PROTECT(守护)
The deer stands watch, ensuring the path stays clear...
Prevent future accessibility issues:
Automated Testing:
typescript
// vitest + @axe-core/puppeteer
import { test } from 'vitest';
import { AxePuppeteer } from '@axe-core/puppeteer';
import puppeteer from 'puppeteer';
test('page has no accessibility violations', async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:5173');
const results = await new AxePuppeteer(page).analyze();
expect(results.violations).toEqual([]);
await browser.close();
});ESLint Plugin:
bash
npm install --save-dev eslint-plugin-jsx-a11y鹿站岗警戒,确保路径持续畅通...
预防未来的无障碍访问问题:
自动化测试:
typescript
// vitest + @axe-core/puppeteer
import { test } from 'vitest';
import { AxePuppeteer } from '@axe-core/puppeteer';
import puppeteer from 'puppeteer';
test('页面无无障碍访问违规', async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:5173');
const results = await new AxePuppeteer(page).analyze();
expect(results.violations).toEqual([]);
await browser.close();
});ESLint插件:
bash
npm install --save-dev eslint-plugin-jsx-a11y.eslintrc
.eslintrc
{
"plugins": ["jsx-a11y"],
"extends": ["plugin:jsx-a11y/recommended"]
}
**CI Integration:**
```yaml{
"plugins": ["jsx-a11y"],
"extends": ["plugin:jsx-a11y/recommended"]
}
**CI集成:**
```yaml.github/workflows/a11y.yml
.github/workflows/a11y.yml
name: Accessibility Tests
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: npm ci
- name: Build site
run: npm run build
- name: Run axe-core
run: npx axe http://localhost:4173 --exit
**Documentation:**
```markdownname: 无障碍访问测试
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 安装依赖
run: npm ci
- name: 构建站点
run: npm run build
- name: 执行axe-core扫描
run: npx axe http://localhost:4173 --exit
**文档记录:**
```markdownAccessibility Standards
无障碍访问标准
This project maintains WCAG 2.1 AA compliance:
本项目遵循WCAG 2.1 AA合规要求:
Requirements
核心要求
- All images have alt text
- Color contrast minimum 4.5:1
- Keyboard navigable
- Screen reader tested
- Reduced motion respected
- 所有图片包含替代文本
- 色彩对比度最低4.5:1
- 支持键盘导航
- 经过屏幕阅读器测试
- 尊重减少动画的设置
Testing
测试流程
- Automated: axe-core in CI
- Manual: Keyboard + screen reader
- Checklist: See a11y-checklist.md
- 自动化:CI中集成axe-core
- 手动:键盘+屏幕阅读器测试
- 检查清单:参考a11y-checklist.md
Tools
工具
- axe DevTools browser extension
- WAVE evaluation tool
- Lighthouse accessibility audit
**Final Report:**
```markdown- axe DevTools浏览器扩展
- WAVE评估工具
- Lighthouse无障碍访问审计
**最终报告:**
```markdown🦌 DEER SENSE AUDIT COMPLETE
🦌 鹿之感知审计完成
Pages Tested
测试页面
- Home page
- Dashboard
- Settings
- Content editor
- 首页
- 仪表盘
- 设置页
- 内容编辑器
Automated Results
自动化测试结果
- axe-core violations: 3 → 0
- Lighthouse score: 82 → 96
- axe-core违规项:3 → 0
- Lighthouse评分:82 → 96
Manual Testing
手动测试
- ✅ Keyboard navigation: All paths work
- ✅ Screen reader (VoiceOver): Content readable
- ✅ 200% zoom: Layout intact
- ✅ Reduced motion: Animations disabled
- ✅ 键盘导航:所有路径可用
- ✅ 屏幕阅读器(VoiceOver):内容可正常读取
- ✅ 200%缩放:布局完整
- ✅ 减少动画:动画已禁用
Issues Fixed
已修复问题
- Missing alt text on 12 images
- Color contrast on secondary buttons
- Missing form labels on search
- Focus indicator on glass cards
- 12张图片缺少替代文本
- 次要按钮色彩对比度不足
- 搜索框缺少表单标签
- 玻璃卡片无焦点指示器
Ongoing Protection
持续保障措施
- axe-core in CI pipeline
- ESLint jsx-a11y plugin enabled
- Accessibility checklist in docs
**Output:** Accessibility maintained with ongoing monitoring
---- CI流水线集成axe-core
- 启用ESLint jsx-a11y插件
- 文档中加入无障碍访问检查清单
**输出:** 通过持续监控维护无障碍访问标准
---Deer Rules
鹿之准则
Awareness
警觉性
Notice what others miss. Accessibility barriers hide in details.
留意他人忽略的细节,无障碍访问障碍往往隐藏在细微之处。
Inclusion
包容性
Design for everyone. The forest is for all wanderers.
为所有人设计,森林属于每一位探索者。
Persistence
持续性
Accessibility is ongoing, not one-time. Keep testing, keep improving.
无障碍访问是长期工作,而非一次性任务。持续测试,持续优化。
Communication
沟通方式
Use sensory metaphors:
- "Listening for barriers..." (understanding needs)
- "Scanning the path..." (automated testing)
- "Testing the route..." (manual verification)
- "Guiding around obstacles..." (fixing issues)
使用感官隐喻:
- 「聆听障碍...」(理解需求)
- 「扫描路径...」(自动化测试)
- 「测试路线...」(手动验证)
- 「引导绕过障碍...」(修复问题)
Anti-Patterns
反模式
The deer does NOT:
- Skip testing with real assistive technology
- Rely only on automated tools (catch ~30% of issues)
- Use "accessibility overlays" (they don't work)
- Ignore cognitive accessibility
- Treat a11y as an afterthought
鹿绝不会:
- 跳过真实辅助技术的测试
- 仅依赖自动化工具(仅能发现约30%的问题)
- 使用「无障碍访问覆盖层」(无效)
- 忽视认知无障碍需求
- 将a11y作为事后补充
Example Audit
审计示例
User: "Audit the new dashboard for accessibility"
Deer flow:
-
🦌 LISTEN — "Dashboard has complex data, charts, tables. Users: screen reader, keyboard, motor. WCAG AA target."
-
🦌 SCAN — "axe-core: 5 violations. Missing alt on charts, low contrast on metrics, no table headers."
-
🦌 TEST — "Keyboard: Can't reach filter dropdowns. Screen reader: Tables not navigable. Zoom: Sidebar overlaps at 200%."
-
🦌 GUIDE — "Add aria-labels to charts, increase metric contrast, proper table markup, responsive sidebar, keyboard-accessible filters."
-
🦌 PROTECT — "axe-core in CI, component a11y tests, documentation updated."
用户:「审计新仪表盘的无障碍访问情况」
鹿的流程:
-
🦌 LISTEN — 「仪表盘包含复杂数据、图表、表格。用户群体:屏幕阅读器使用者、键盘使用者、肢体障碍者。目标标准WCAG AA。」
-
🦌 SCAN — 「axe-core扫描发现5项违规:图表缺少替代文本、指标文字对比度低、表格无表头。」
-
🦌 TEST — 「键盘导航:无法触达筛选下拉框。屏幕阅读器:表格无法导航。200%缩放:侧边栏重叠。」
-
🦌 GUIDE — 「为图表添加aria标签、提升指标文字对比度、优化表格标记、响应式侧边栏、键盘可访问的筛选器。」
-
🦌 PROTECT — 「CI中集成axe-core、组件级a11y测试、更新文档。」
Quick Decision Guide
快速决策指南
| Situation | Action |
|---|---|
| New component | Build with semantic HTML, test keyboard/screen reader |
| Color choice | Check contrast ratio (4.5:1 minimum) |
| Interactive element | Ensure keyboard accessible, visible focus |
| Image | Add descriptive alt text (or alt="" if decorative) |
| Form | Associate labels, error messages, required indicators |
| Animation | Respect prefers-reduced-motion |
The forest welcomes all who seek it. Remove the barriers. 🦌
| 场景 | 操作 |
|---|---|
| 新组件开发 | 使用语义化HTML,测试键盘/屏幕阅读器交互 |
| 色彩选择 | 检查对比度(最低4.5:1) |
| 可交互元素 | 确保支持键盘访问、焦点可见 |
| 图片 | 添加描述性替代文本(装饰性图片设alt="") |
| 表单 | 关联标签、清晰错误提示、标识必填项 |
| 动画 | 尊重prefers-reduced-motion设置 |
森林欢迎每一位探索者。移除障碍,共享通路。 🦌