소스 정보
- 저장소
- tomevault-io/skills-registry
- 최근 소스 활동
- 2026년 4월 28일 22:53
- 감지된 SKILL.md 언어
- 영어
- 스타
- 0
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill accessibility-testing명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | accessibility-testing |
| description | > Use when this capability is needed. |
Comprehensive guide for integrating accessibility (a11y) testing into your Playwright test automation.
npm install --save-dev @axe-core/playwright
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('homepage should not have accessibility violations', async ({ page }) => {
await page.goto('https://your-app.com');
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility Checks', () => {
test('prescription search page should be accessible', async ({ page }) => {
await page.goto('/prescriptions/search');
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
test('refill confirmation page should be accessible', async ({ page }) => {
await page.goto('/prescriptions/refill/confirm');
const accessibilityScanResults = await new AxeBuilder({ page })
.exclude('#third-party-widget') // Exclude elements you don't control
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
});
test('user can navigate prescription list with keyboard', async ({ page }) => {
await page.goto('/prescriptions');
// Tab to first prescription
await page.keyboard.press('Tab');
const firstPrescription = page.getByRole('article').first();
await expect(firstPrescription).toBeFocused();
// Navigate with arrow keys
await page.keyboard.press('ArrowDown');
const secondPrescription = page.getByRole('article').nth(1);
await expect(secondPrescription).toBeFocused();
// Activate with Enter
await page.keyboard.press('Enter');
await expect(page).toHaveURL(/.*prescription\/[0-9]+/);
});
test('modal can be closed with Escape key', async ({ page }) => {
await page.goto('/prescriptions');
// Open modal
await page.(, { : }).();
modal = page.();
(modal).();
page..();
(modal).();
});
test('images have alt text', async ({ page }) => {
await page.goto('/prescriptions');
const images = page.getByRole('img');
const count = await images.count();
for (let i = 0; i < count; i++) {
const img = images.nth(i);
await expect(img).toHaveAttribute('alt');
}
});
test('form inputs have accessible labels', async ({ page }) => {
await page.goto('/patient/registration');
// All inputs should be accessible by label
await expect(page.getByLabel('First name')).toBeVisible();
await expect(page.getByLabel('Last name')).toBeVisible();
await expect(page.getByLabel('Email')).toBeVisible();
await expect(page.getByLabel('Phone')).toBeVisible();
});
(, ({ page }) => {
page.();
buttons = page.();
count = buttons.();
( i = ; i < count; i++) {
button = buttons.(i);
accessibleName = button.() || button.();
(accessibleName).();
}
});
test('text has sufficient color contrast', async ({ page }) => {
await page.goto('/dashboard');
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2aa'])
.analyze();
// Check for color contrast violations
const contrastViolations = accessibilityScanResults.violations.filter(
v => v.id === 'color-contrast'
);
expect(contrastViolations).toEqual([]);
});
test('focus moves to error message after validation failure', async ({ page }) => {
await page.goto('/prescriptions/refill');
// Submit form without filling required fields
await page.getByRole('button', { name: 'Submit' }).click();
// Focus should move to error message or first invalid field
const errorMessage = page.getByRole('alert');
await expect(errorMessage).toBeFocused();
});
test('focus is trapped in modal dialog', async ({ page }) => {
await page.goto('/prescriptions');
await page.getByRole('button', { name: 'Delete' }).click();
const modal = page.getByRole('dialog');
const confirmButton = modal.getByRole('button', { name: 'Confirm' });
const cancelButton = modal.getByRole('button', { name: 'Cancel' });
// Tab through modal elements
await page..();
(confirmButton).();
page..();
(cancelButton).();
page..();
(confirmButton).();
});
// accessibility-suite.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility Compliance - Prescription Management', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/prescriptions');
});
test('automated accessibility scan', async ({ page }) => {
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});
test('keyboard navigation', async ({ page }) => {
await page.keyboard.press('Tab');
await expect(page.getByRole('link', { name: 'Skip to main content' })).toBeFocused();
await page.keyboard.();
(page.(, { : })).();
});
(, ({ page }) => {
(page.()).();
(page.()).();
(page.()).();
(page.()).();
});
(, ({ page }) => {
page.(, { : }).();
(page.()).();
(page.()).();
});
});
<!-- ❌ Bad -->
<img src="prescription.jpg">
<!-- ✅ Good -->
<img src="prescription.jpg" alt="Lisinopril 10mg prescription">
/* ❌ Bad - Low contrast */
.text {
color: #999999;
background: #ffffff;
}
/* ✅ Good - High contrast */
.text {
color: #333333;
background: #ffffff;
}
<!-- ❌ Bad - Div as button -->
<div onclick="submit()">Submit</div>
<!-- ✅ Good - Semantic button -->
<button type="submit">Submit</button>
<!-- ❌ Bad - No label -->
<input type="text" name="email" placeholder="Email">
<!-- ✅ Good - Proper label -->
<label for="email">Email</label>
<input type="text" id="email" name="email">
Playwright provides built-in matchers for element-level accessibility checks — no axe-core needed. Use these for targeted regression testing alongside full-page scans.
Verifies an element's accessible name (what screen readers announce).
test('buttons have correct accessible names', async ({ page }) => {
await page.goto('/prescriptions');
// Verify accessible names on interactive elements
await expect(page.getByRole('button', { name: 'Refill' }))
.toHaveAccessibleName('Refill prescription');
await expect(page.getByRole('link', { name: 'View details' }))
.toHaveAccessibleName('View details for Lisinopril 10mg');
// Icon-only button should have an accessible name via aria-label
await expect(page.getByTestId('close-btn'))
.toHaveAccessibleName('Close dialog');
});
test('form inputs have proper accessible names', async ({ page }) => {
await page.goto('/patient/registration');
await expect(page.getByRole('textbox', { name: 'First name' }))
.toHaveAccessibleName('First name');
await expect(page.getByRole(, { : }))
.();
(page.().())
.();
});
Verifies the element's accessible description (additional context for screen readers, often from aria-describedby).
test('form fields have helpful descriptions', async ({ page }) => {
await page.goto('/patient/registration');
// Password field should describe requirements
await expect(page.getByLabel('Password'))
.toHaveAccessibleDescription('Must be at least 8 characters with one number');
// Date field should describe format
await expect(page.getByLabel('Date of birth'))
.toHaveAccessibleDescription(/MM\/DD\/YYYY/);
});
Verifies error messages are properly associated with form inputs via aria-errormessage.
test('form validation shows accessible error messages', async ({ page }) => {
await page.goto('/patient/registration');
// Submit empty form to trigger validation
await page.getByRole('button', { name: 'Register' }).click();
// Verify error messages are programmatically associated with inputs
await expect(page.getByLabel('Email address'))
.toHaveAccessibleErrorMessage('Email is required');
await expect(page.getByLabel('Password'))
.toHaveAccessibleErrorMessage('Password must be at least 8 characters');
// After fixing the error, error message should clear
await page.getByLabel('Email address').fill('user@example.com');
await page.getByLabel('Email address').blur();
await expect(page.getByLabel('Email address'))
.not.toHaveAccessibleErrorMessage();
});
test.describe('Comprehensive A11y: Scan + Element Assertions', () => {
test('login form is fully accessible', async ({ page }) => {
await page.goto('/login');
// 1. Full page scan with axe-core
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(results.violations).toEqual([]);
// 2. Element-level assertions for regression safety
await expect(page.getByRole('textbox', { name: 'Email' }))
.toHaveAccessibleName('Email');
await expect(page.getByLabel('Password'))
.toHaveAccessibleDescription(/at least 8 characters/);
await expect(page.getByRole('button', { name: 'Sign in' }))
.toHaveAccessibleName('Sign in');
});
});
axe-playwright offers a simpler API than @axe-core/playwright. Good for teams that want minimal setup.
npm install --save-dev axe-playwright
import { test, expect } from '@playwright/test';
import { injectAxe, checkA11y, getViolations } from 'axe-playwright';
test.describe('A11y with axe-playwright', () => {
test('homepage is accessible', async ({ page }) => {
await page.goto('/');
// Step 1: Inject axe-core into the page
await injectAxe(page);
// Step 2: Run accessibility check (auto-fails on violations)
await checkA11y(page);
});
test('scoped scan on specific element', async ({ page }) => {
await page.goto('/prescriptions');
await injectAxe(page);
// Check only the main content area
await checkA11y(page, '#main-content', {
axeOptions: {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa'],
},
},
});
});
test('get violations for custom reporting', async ({ page }) => {
await page.goto('/dashboard');
(page);
violations = (page);
(violations. > ) {
report = violations.( ({
: v.,
: v.,
: v.,
: v..,
}));
.(report);
}
(violations).();
});
});
| Feature | @axe-core/playwright | axe-playwright |
|---|---|---|
| Maintainer | Deque Systems (official) | Community |
| API style | Builder pattern (new AxeBuilder()) | Function calls (injectAxe + checkA11y) |
| Setup | Import and use directly | Inject into page first |
| Flexibility | High (include/exclude, tags, rules) | Moderate |
| Auto-fail on violations | No (you assert manually) | Yes (configurable) |
| Recommendation | ✅ Use for production projects | Good for quick checks |
Track a11y violations over time and prevent regressions in your pipeline.
// fixtures/a11y-fixture.ts
import { test as base, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
type A11yFixtures = {
makeAxeBuilder: () => AxeBuilder;
};
export const test = base.extend<A11yFixtures>({
makeAxeBuilder: async ({ page }, use) => {
await use(() =>
new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
);
},
});
export { expect };
// tests/a11y-regression.spec.ts
import { test, expect } from '../fixtures/a11y-fixture';
const criticalPages = [
{ name: 'Home', path: '/' },
{ name: 'Login', path: '/login' },
{ name: 'Dashboard', path: '/dashboard' },
{ name: 'Prescriptions', path: '/prescriptions' },
{ name: 'Profile', path: '/profile' },
{ name: 'Settings', path: '/settings' },
];
for (const { name, path } of criticalPages) {
test(`a11y regression: ${name} page`, async ({ page, makeAxeBuilder }) => {
await page.goto(path);
const results = await makeAxeBuilder().analyze();
// Attach violations to test report for debugging
if (results.violations.length > 0) {
const violationSummary = results.violations.map( => ({
: v.,
: v.,
: v.,
: v..,
}));
.(, .(violationSummary, , ));
}
(results.).([]);
});
}
test('full a11y audit @a11y @regression', async ({ page, makeAxeBuilder }) => {
await page.goto('/');
const results = await makeAxeBuilder().analyze();
expect(results.violations).toEqual([]);
});
# Run only a11y tests in CI
npx playwright test --grep @a11y
# Run a11y tests nightly (not on every PR)
npx playwright test --grep @a11y --project=chromium
Converted and distributed by TomeVault — claim your Tome and manage your conversions.