豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content
This repository was archived by the owner on Apr 16, 2026. It is now read-only.

Commit b0c6d04

Browse files
authored
feat: add a new skill for accessibility debugging and auditing with Chrome DevTools MCP. (ChromeDevTools#1002)
1 parent 0d78685 commit b0c6d04

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

skills/a11y-debugging/SKILL.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
---
2+
name: a11y-debugging
3+
description: Uses Chrome DevTools MCP for accessibility (a11y) debugging and auditing based on web.dev guidelines. Use when testing semantic HTML, ARIA labels, focus states, keyboard navigation, tap targets, and color contrast.
4+
---
5+
6+
## Core Concepts
7+
8+
**Accessibility Tree vs DOM**: Visually hiding an element (e.g., `CSS opacity: 0`) behaves differently for screen readers than `display: none` or `aria-hidden="true"`. The `take_snapshot` tool returns the accessibility tree of the page, which represents what assistive technologies "see", making it the most reliable source of truth for semantic structure.
9+
10+
**Reading web.dev documentation**: If you need to research specific accessibility guidelines (like `https://web.dev/articles/accessible-tap-targets`), you can append `.md.txt` to the URL (e.g., `https://web.dev/articles/accessible-tap-targets.md.txt`) to fetch the clean, raw markdown version. This is much easier to read using the `read_url_content` tool!
11+
12+
## Workflow Patterns
13+
14+
### 1. Browser Issues & Audits
15+
16+
Chrome automatically checks for common accessibility problems. Use `list_console_messages` to check for these native audits first:
17+
18+
- `types`: `["issue"]`
19+
- `includePreservedMessages`: `true` (to catch issues that occurred during page load)
20+
21+
This often reveals missing labels, invalid ARIA attributes, and other critical errors without manual investigation.
22+
23+
### 2. Semantics & Structure
24+
25+
The accessibility tree exposes the heading hierarchy and semantic landmarks.
26+
27+
1. Navigate to the page.
28+
2. Use `take_snapshot` to capture the accessibility tree.
29+
3. **Check Heading Levels**: Ensure heading levels (`h1`, `h2`, `h3`, etc.) are logical and do not skip levels. The snapshot will include heading roles.
30+
4. **Content Reordering**: Verify that the DOM order (which drives the accessibility tree) matches the visual reading order. Use `take_screenshot` to inspect the visual layout and compare it against the snapshot structure to catch CSS floats or absolute positioning that jumbles the logical flow.
31+
32+
### 3. Labels, Forms & Text Alternatives
33+
34+
1. Locate buttons, inputs, and images in the `take_snapshot` output.
35+
2. Ensure interactive elements have an accessible name (e.g., a button should not just say `""` if it only contains an icon).
36+
3. **Orphaned Inputs**: Verify that all form inputs have associated labels. Use `evaluate_script` to check for inputs missing `id` (for `label[for]`) or `aria-label`:
37+
```javascript
38+
() => {
39+
const inputs = Array.from(
40+
document.querySelectorAll('input, select, textarea'),
41+
);
42+
return inputs
43+
.filter(i => {
44+
const hasId = i.id && document.querySelector(`label[for="${i.id}"]`);
45+
const hasAria =
46+
i.getAttribute('aria-label') || i.getAttribute('aria-labelledby');
47+
const hasImplicitLabel = i.closest('label');
48+
return !hasId && !hasAria && !hasImplicitLabel;
49+
})
50+
.map(i => ({
51+
tag: i.tagName,
52+
id: i.id,
53+
name: i.name,
54+
placeholder: i.placeholder,
55+
}));
56+
};
57+
```
58+
59+
````
60+
61+
4. Check images for `alt` text.
62+
63+
### 4. Focus & Keyboard Navigation
64+
65+
Testing "keyboard traps" and proper focus management without visual feedback relies on tracking the focused element.
66+
67+
1. Use the `press_key` tool with `"Tab"` or `"Shift+Tab"` to move focus.
68+
2. Use `take_snapshot` to capture the updated accessibility tree.
69+
3. Locate the element marked as focused in the snapshot to verify focus moved to the expected interactive element.
70+
4. If a modal opens, focus must move into the modal and "trap" within it until closed.
71+
72+
### 5. Tap Targets and Visuals
73+
74+
According to web.dev, tap targets should be at least 48x48 pixels with sufficient spacing. Since the accessibility tree doesn't show sizes, use `evaluate_script`:
75+
76+
```javascript
77+
// Usage in console: copy, paste, and call with element: fn(element)
78+
el => {
79+
const rect = el.getBoundingClientRect();
80+
return {width: rect.width, height: rect.height};
81+
};
82+
````
83+
84+
_Pass the element's `uid` from the snapshot as an argument to the tool._
85+
86+
### 6. Color Contrast
87+
88+
To verify color contrast ratios, start by checking for native accessibility issues:
89+
90+
1. Call `list_console_messages` with `types: ["issue"]`.
91+
2. Look for "Low Contrast" issues in the output.
92+
93+
If native audits do not report issues (which may happen in some headless environments) or if you need to check a specific element manually, you can use the following script as a fallback approximation.
94+
95+
**Note**: This script uses a simplified algorithm and may not account for transparency, gradients, or background images. For production-grade auditing, consider injecting `axe-core`.
96+
97+
```javascript
98+
el => {
99+
function getRGB(colorStr) {
100+
const match = colorStr.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
101+
return match
102+
? [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]
103+
: [255, 255, 255];
104+
}
105+
function luminance(r, g, b) {
106+
const a = [r, g, b].map(function (v) {
107+
v /= 255;
108+
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
109+
});
110+
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
111+
}
112+
113+
const style = window.getComputedStyle(el);
114+
const fg = getRGB(style.color);
115+
let bg = getRGB(style.backgroundColor);
116+
117+
// Basic contrast calculation (Note: Doesn't account for transparency over background images)
118+
const l1 = luminance(fg[0], fg[1], fg[2]);
119+
const l2 = luminance(bg[0], bg[1], bg[2]);
120+
const ratio = (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
121+
122+
return {
123+
color: style.color,
124+
bg: style.backgroundColor,
125+
contrastRatio: ratio.toFixed(2),
126+
};
127+
};
128+
```
129+
130+
_Pass the element's `uid` to test the contrast against WCAG AA (4.5:1 for normal text, 3:1 for large text)._
131+
132+
### 7. Global Page Checks
133+
134+
Verify document-level accessibility settings often missed in component testing:
135+
136+
```javascript
137+
(() => {
138+
const f = () => {
139+
return {
140+
lang:
141+
document.documentElement.lang ||
142+
'MISSING - Screen readers need this for pronunciation',
143+
title: document.title || 'MISSING - Required for context',
144+
viewport:
145+
document.querySelector('meta[name="viewport"]')?.content ||
146+
'MISSING - Check for user-scalable=no (bad practice)',
147+
reducedMotion: window.matchMedia('(prefers-reduced-motion: reduce)')
148+
.matches
149+
? 'Enabled'
150+
: 'Disabled',
151+
};
152+
};
153+
try {
154+
console.log(f());
155+
} catch (e) {} // Log for manual console usage
156+
return f;
157+
})();
158+
```
159+
160+
## Troubleshooting
161+
162+
If standard a11y queries fail or the `evaluate_script` snippets return unexpected results:
163+
164+
- **Visual Inspection**: If automated scripts cannot determine contrast (e.g., text over gradient images or complex backgrounds), use `take_screenshot` to capture the element. While models cannot measure exact contrast ratios from images, they can visually assess legibility and identifying obvious issues.

0 commit comments

Comments
 (0)