007
無料Security audit, hardening, threat modeling (STRIDE/PASTA), Red/Blue Team, OWASP checks, code review, incident response, and infrastructure security for any project.
日本語の概要は準備中です。原文の説明を表示しています。
Comprehensive WCAG compliance and accessibility testing covering ARIA, keyboard navigation, screen readers, color contrast, and automated a11y validation.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
You are an expert accessibility engineer specializing in WCAG compliance and inclusive web design. When asked to test or improve accessibility, follow these comprehensive instructions.
Level A (Minimum)
- Basic accessibility features
- Essential for some users
- Examples: Alt text, keyboard access, labels
Level AA (Standard)
- Recommended baseline for most sites
- Addresses major barriers
- Examples: Color contrast 4.5:1, focus indicators, skip links
Level AAA (Enhanced)
- Highest accessibility standard
- Not always achievable for all content
- Examples: Color contrast 7:1, sign language, extended descriptions
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
});
npm install --save-dev @axe-core/playwright
// tests/accessibility.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility tests', () => {
test('should not have any automatically detectable accessibility issues', async ({ page }) => {
await page.goto('/');
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
test('should have accessible homepage', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.exclude('#third-party-widget') // Exclude third-party content
.analyze();
// Log violations for debugging
if (results.violations.length > 0) {
console.log('Accessibility violations:', JSON.stringify(results.violations, null, 2));
}
expect(results.violations).toEqual([]);
});
test('should have accessible forms', async ({ page }) => {
await page.goto('/contact');
const results = await new AxeBuilder({ page })
.include('form') // Test only forms
.analyze();
expect(results.violations).toEqual([]);
});
test('should meet specific WCAG rules', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withRules(['color-contrast', 'image-alt', 'label', 'aria-required-attr'])
.analyze();
expect(results.violations).toEqual([]);
});
});
// cypress/support/commands.ts
import 'cypress-axe';
Cypress.Commands.add('checkA11y', (context?: string, options?: any) => {
cy.injectAxe();
cy.checkA11y(context, options, (violations) => {
if (violations.length) {
cy.task('log', violations);
}
});
});
// cypress/e2e/accessibility.cy.ts
describe('Accessibility', () => {
beforeEach(() => {
cy.visit('/');
cy.injectAxe();
});
it('should have no accessibility violations on homepage', () => {
cy.checkA11y();
});
it('should have accessible navigation', () => {
cy.checkA11y('nav');
});
it('should meet WCAG AA color contrast', () => {
cy.checkA11y(null, {
rules: {
'color-contrast': { enabled: true },
},
});
});
});
test.describe('Keyboard navigation', () => {
test('should navigate through interactive elements with Tab', async ({ page }) => {
await page.goto('/');
// Start at the first focusable element
await page.keyboard.press('Tab');
const firstFocusedElement = await page.evaluate(() => document.activeElement?.tagName);
expect(['A', 'BUTTON', 'INPUT']).toContain(firstFocusedElement);
// Tab through all interactive elements
for (let i = 0; i < 5; i++) {
await page.keyboard.press('Tab');
const focused = await page.evaluate(() => {
const el = document.activeElement;
return {
tag: el?.tagName,
visible: el ? window.getComputedStyle(el).display !== 'none' : false,
};
});
expect(focused.visible).toBe(true);
}
});
test('should submit form with Enter key', async ({ page }) => {
await page.goto('/contact');
await page.fill('#name', 'Test User');
await page.fill('#email', 'test@example.com');
await page.fill('#message', 'Test message');
// Focus on submit button and press Enter
await page.focus('button[type="submit"]');
await page.keyboard.press('Enter');
await expect(page.getByText('Message sent')).toBeVisible();
});
test('should close modal with Escape key', async ({ page }) => {
await page.goto('/');
await page.click('button[aria-label="Open modal"]');
await expect(page.getByRole('dialog')).toBeVisible();
await page.keyboard.press('Escape');
await expect(page.getByRole('dialog')).not.toBeVisible();
});
test('should skip to main content with skip link', async ({ page }) => {
await page.goto('/');
// Tab to skip link
await page.keyboard.press('Tab');
const skipLink = page.getByText('Skip to main content');
await expect(skipLink).toBeFocused();
// Activate skip link
await page.keyboard.press('Enter');
// Main content should now be focused
const mainContent = page.locator('main');
await expect(mainContent).toBeFocused();
});
});
test.describe('Focus management', () => {
test('should have visible focus indicators', async ({ page }) => {
await page.goto('/');
await page.keyboard.press('Tab');
const focusedElement = page.locator(':focus');
// Check that focused element has visible outline or custom focus styles
const styles = await focusedElement.evaluate((el) => {
const computed = window.getComputedStyle(el);
return {
outline: computed.outline,
outlineWidth: computed.outlineWidth,
boxShadow: computed.boxShadow,
};
});
// Should have either outline or box-shadow for focus
expect(
styles.outlineWidth !== '0px' ||
styles.boxShadow !== 'none'
).toBe(true);
});
test('should trap focus inside modal', async ({ page }) => {
await page.goto('/');
await page.click('button[aria-label="Open modal"]');
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible();
// Tab through modal elements
await page.keyboard.press('Tab');
const firstFocusable = await page.evaluate(() => document.activeElement?.id);
// Keep tabbing until we cycle back
for (let i = 0; i < 10; i++) {
await page.keyboard.press('Tab');
}
const currentFocus = await page.evaluate(() => document.activeElement?.id);
// Focus should cycle within modal, not escape to body
const focusedParent = await page.evaluate(() =>
document.activeElement?.closest('[role="dialog"]') !== null
);
expect(focusedParent).toBe(true);
});
});
test.describe('Screen reader support', () => {
test('should have proper ARIA labels', async ({ page }) => {
await page.goto('/');
// Check navigation has aria-label
const nav = page.locator('nav');
const ariaLabel = await nav.getAttribute('aria-label');
expect(ariaLabel).toBeTruthy();
// Check buttons have accessible names
const buttons = page.locator('button');
const count = await buttons.count();
for (let i = 0; i < count; i++) {
const button = buttons.nth(i);
const accessibleName = await button.evaluate((el) =>
(el as HTMLElement).ariaLabel ||
(el as HTMLElement).innerText ||
(el as HTMLElement).title
);
expect(accessibleName).toBeTruthy();
}
});
test('should announce page regions correctly', async ({ page }) => {
await page.goto('/');
// Check for landmark regions
const landmarks = await page.evaluate(() => {
return {
header: document.querySelector('header')?.getAttribute('role') || 'banner',
nav: document.querySelector('nav')?.getAttribute('role') || 'navigation',
main: document.querySelector('main')?.getAttribute('role') || 'main',
footer: document.querySelector('footer')?.getAttribute('role') || 'contentinfo',
};
});
expect(landmarks.header).toBeTruthy();
expect(landmarks.nav).toBeTruthy();
expect(landmarks.main).toBeTruthy();
expect(landmarks.footer).toBeTruthy();
});
test('should have accessible image alt text', async ({ page }) => {
await page.goto('/');
const images = page.locator('img');
const count = await images.count();
for (let i = 0; i < count; i++) {
const img = images.nth(i);
const alt = await img.getAttribute('alt');
const role = await img.getAttribute('role');
// Images should have alt text or role="presentation" for decorative images
expect(alt !== null || role === 'presentation').toBe(true);
}
});
test('should use ARIA live regions for dynamic content', async ({ page }) => {
await page.goto('/notifications');
// Trigger a notification
await page.click('button[aria-label="Show notification"]');
const liveRegion = page.locator('[aria-live="polite"]');
await expect(liveRegion).toHaveText('Notification message');
});
});
test.describe('Color contrast', () => {
test('should meet WCAG AA contrast ratio for text', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withRules(['color-contrast'])
.analyze();
expect(results.violations).toEqual([]);
});
test('should be readable in high contrast mode', async ({ page }) => {
await page.emulateMedia({ colorScheme: 'dark', forcedColors: 'active' });
await page.goto('/');
// Check that text is visible
const heading = page.getByRole('heading', { level: 1 });
await expect(heading).toBeVisible();
});
});
test.describe('Form accessibility', () => {
test('should have proper labels for inputs', async ({ page }) => {
await page.goto('/contact');
const inputs = page.locator('input, textarea, select');
const count = await inputs.count();
for (let i = 0; i < count; i++) {
const input = inputs.nth(i);
const id = await input.getAttribute('id');
const ariaLabel = await input.getAttribute('aria-label');
const ariaLabelledBy = await input.getAttribute('aria-labelledby');
// Input should have associated label
const hasLabel = id
? await page.locator(`label[for="${id}"]`).count() > 0
: false;
expect(hasLabel || ariaLabel || ariaLabelledBy).toBeTruthy();
}
});
test('should show validation errors accessibly', async ({ page }) => {
await page.goto('/contact');
// Submit form without filling required fields
await page.click('button[type="submit"]');
// Error message should be announced
const errorMessage = page.locator('[role="alert"]');
await expect(errorMessage).toBeVisible();
// Invalid field should have aria-invalid
const emailInput = page.locator('#email');
const ariaInvalid = await emailInput.getAttribute('aria-invalid');
expect(ariaInvalid).toBe('true');
// Error should be associated with field
const ariaDescribedBy = await emailInput.getAttribute('aria-describedby');
expect(ariaDescribedBy).toBeTruthy();
});
test('should have accessible required field indicators', async ({ page }) => {
await page.goto('/contact');
const requiredInputs = page.locator('[required]');
const count = await requiredInputs.count();
for (let i = 0; i < count; i++) {
const input = requiredInputs.nth(i);
const ariaRequired = await input.getAttribute('aria-required');
expect(ariaRequired).toBe('true');
}
});
});
<!-- Good: Button with accessible name -->
<button aria-label="Close dialog">×</button>
<!-- Good: Button with text content -->
<button>Submit</button>
<!-- Bad: No accessible name -->
<button><span class="icon-close"></span></button>
<!-- Modal with proper ARIA -->
<div
role="dialog"
aria-labelledby="modal-title"
aria-describedby="modal-description"
aria-modal="true"
>
<h2 id="modal-title">Confirm Action</h2>
<p id="modal-description">Are you sure you want to proceed?</p>
<button>Confirm</button>
<button>Cancel</button>
</div>
test('should implement accessible tabs', async ({ page }) => {
await page.goto('/tabs-demo');
// Tab list should have role="tablist"
const tablist = page.getByRole('tablist');
await expect(tablist).toBeVisible();
// Individual tabs should have role="tab"
const firstTab = page.getByRole('tab', { name: 'Tab 1' });
await expect(firstTab).toHaveAttribute('aria-selected', 'true');
// Tab panels should have role="tabpanel"
const firstPanel = page.getByRole('tabpanel', { name: 'Tab 1' });
await expect(firstPanel).toBeVisible();
// Arrow keys should navigate tabs
await firstTab.focus();
await page.keyboard.press('ArrowRight');
const secondTab = page.getByRole('tab', { name: 'Tab 2' });
await expect(secondTab).toBeFocused();
await expect(secondTab).toHaveAttribute('aria-selected', 'true');
});
test('should implement accessible autocomplete', async ({ page }) => {
await page.goto('/search');
const combobox = page.getByRole('combobox');
await expect(combobox).toHaveAttribute('aria-expanded', 'false');
// Type to trigger autocomplete
await combobox.fill('test');
await expect(combobox).toHaveAttribute('aria-expanded', 'true');
// Listbox should appear
const listbox = page.getByRole('listbox');
await expect(listbox).toBeVisible();
// Navigate with arrow keys
await page.keyboard.press('ArrowDown');
const firstOption = page.getByRole('option').first();
await expect(firstOption).toHaveAttribute('aria-selected', 'true');
});
<html lang="en">)alt="" or role="presentation"<button> over <div role="button">.role="button" on <div> -- Use native <button>.outline: none without replacement.Automated Testing:
Manual Testing:
Learning Resources:
Accessibility is not optional. Building inclusive experiences makes the web better for everyone.
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Security audit, hardening, threat modeling (STRIDE/PASTA), Red/Blue Team, OWASP checks, code review, incident response, and infrastructure security for any project.
日本語の概要は準備中です。原文の説明を表示しています。
Guides the creation of agile user stories and Gherkin feature files. Use when the user wants to create a user story, write acceptance criteria, define Gherkin scenarios, or author BDD feature files. This should trigger for requests such as Create a user story; Write a user story; I need to write a user story. Part of cursor-rules-java project
日本語の概要は準備中です。原文の説明を表示しています。
Facilitates conversational discovery to create Architectural Decision Records (ADRs) for non-functional requirements using the ISO/IEC 25010:2023 quality model. Use when the user wants to document quality attributes, NFR decisions, security/performance/scalability architecture, or design systems with measurable quality criteria. This should trigger for requests such as Create ADR for Non-functional requirements; Document Non-functional requirements; Capture Non-functional requirements; Generate Non-functional requirements in an ADR. Part of cursor-rules-java project
日本語の概要は準備中です。原文の説明を表示しています。
Run a health check on an existing project: dependency audit, security scan, test runner detection, CI/CD evaluation, and missing configuration analysis. Maps the three execution gates (pre/in/post) from /10x-bootstrapper to an assessment framework for existing codebases. Reads optional context/foundation/stack-assessment.md from /10x-stack-assess to focus checks on identified gaps. Writes context/foundation/health-check.md with findings, prioritized fixes, and an agent-readiness verdict. Use when the user has an existing project and wants to verify its health before working with an agent. Trigger phrases: "health check", "check my project", "audit my project", "is my project healthy", "sprawdź projekt", "audyt projektu", "health-check", "project health". Use AFTER /10x-stack-assess (brownfield chain), BEFORE agent onboarding (m1-l4).
日本語の概要は準備中です。原文の説明を表示しています。
You MUST use this when building projects end-to-end. Orchestrates all 12 team roles — automatically switches between CTO, architect, PM, engineers, SRE, security, DBA, QA, and EM based on the current phase of work. Starts with brainstorming before any implementation.
日本語の概要は準備中です。原文の説明を表示しています。
Use when you need to add or configure Maven plugins in your pom.xml — including quality tools (enforcer, surefire, failsafe, jacoco, pitest, spotbugs, pmd), security scanning (OWASP), code formatting (Spotless), version management, container image build (Jib), build information tracking, and benchmarking (JMH) — through a consultative, modular step-by-step approach that only adds what you actually need. This should trigger for requests such as Add Maven plugins in pom.xml; Improve Maven plugins in pom.xml. Part of cursor-rules-java project
日本語の概要は準備中です。原文の説明を表示しています。