007
無料Security audit, hardening, threat modeling (STRIDE/PASTA), Red/Blue Team, OWASP checks, code review, incident response, and infrastructure security for any project.
日本語の概要は準備中です。原文の説明を表示しています。
Implement WCAG 2.1 AA accessibility compliance with ARIA labels, keyboard navigation, screen reader support, and color contrast. Use when ensuring accessibility or fixing a11y issues.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
You implement WCAG 2.1 AA accessibility compliance for the QA Team Portal.
Use proper HTML5 elements:
// ❌ Wrong: Divs for everything
<div className="button" onClick={handleClick}>Click me</div>
<div className="nav">
<div>Home</div>
<div>About</div>
</div>
// ✅ Correct: Semantic elements
<button onClick={handleClick}>Click me</button>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
// ✅ Proper document structure
<header>
<nav>...</nav>
</header>
<main>
<article>
<h1>Page Title</h1>
<section>
<h2>Section Title</h2>
<p>Content</p>
</section>
</article>
</main>
<footer>...</footer>
Location: frontend/src/components/Navigation.tsx
export const Navigation = () => {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
return (
<header role="banner">
<nav role="navigation" aria-label="Main navigation">
<div className="container">
<a href="/" aria-label="QA Team Portal Home">
<img src="/logo.svg" alt="Evoke Logo" />
<span>QA Team Portal</span>
</a>
{/* Desktop Menu */}
<ul role="menubar" className="hidden md:flex">
<li role="none">
<a href="#team" role="menuitem">Team</a>
</li>
<li role="none">
<a href="#updates" role="menuitem">Updates</a>
</li>
<li role="none">
<a href="#tools" role="menuitem">Tools</a>
</li>
</ul>
{/* Mobile Menu Toggle */}
<button
className="md:hidden"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
aria-expanded={mobileMenuOpen}
aria-controls="mobile-menu"
>
{mobileMenuOpen ? <X /> : <Menu />}
</button>
</div>
{/* Mobile Menu */}
{mobileMenuOpen && (
<div
id="mobile-menu"
role="menu"
aria-label="Mobile navigation"
>
<a href="#team" role="menuitem">Team</a>
<a href="#updates" role="menuitem">Updates</a>
<a href="#tools" role="menuitem">Tools</a>
</div>
)}
</nav>
</header>
)
}
Focus Management:
// frontend/src/components/Modal.tsx
import { useEffect, useRef } from 'react'
export const Modal = ({ isOpen, onClose, children }) => {
const modalRef = useRef<HTMLDivElement>(null)
const previousFocusRef = useRef<HTMLElement | null>(null)
useEffect(() => {
if (isOpen) {
// Store previous focus
previousFocusRef.current = document.activeElement as HTMLElement
// Focus first focusable element in modal
const focusableElements = modalRef.current?.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
if (focusableElements && focusableElements.length > 0) {
(focusableElements[0] as HTMLElement).focus()
}
// Trap focus inside modal
const handleTab = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return
const focusableContent = modalRef.current?.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
)
if (!focusableContent || focusableContent.length === 0) return
const firstElement = focusableContent[0] as HTMLElement
const lastElement = focusableContent[focusableContent.length - 1] as HTMLElement
if (e.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus()
e.preventDefault()
}
} else {
if (document.activeElement === lastElement) {
firstElement.focus()
e.preventDefault()
}
}
}
document.addEventListener('keydown', handleTab)
return () => {
document.removeEventListener('keydown', handleTab)
// Restore previous focus
previousFocusRef.current?.focus()
}
}
}, [isOpen])
// Close on Escape key
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isOpen) {
onClose()
}
}
document.addEventListener('keydown', handleEscape)
return () => document.removeEventListener('keydown', handleEscape)
}, [isOpen, onClose])
if (!isOpen) return null
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
ref={modalRef}
className="fixed inset-0 z-50"
>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50"
onClick={onClose}
aria-hidden="true"
/>
{/* Modal Content */}
<div className="fixed inset-0 flex items-center justify-center p-4">
<div className="bg-white rounded-lg max-w-md w-full p-6">
<h2 id="modal-title" className="text-2xl font-bold mb-4">
Modal Title
</h2>
{children}
<button onClick={onClose} className="mt-4">
Close
</button>
</div>
</div>
</div>
)
}
Skip to Main Content:
// frontend/src/components/SkipToContent.tsx
export const SkipToContent = () => {
return (
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary focus:text-white focus:rounded"
>
Skip to main content
</a>
)
}
// Usage in App.tsx
<SkipToContent />
<Header />
<main id="main-content">
{/* Page content */}
</main>
Accessible Form:
// frontend/src/components/forms/AccessibleForm.tsx
export const LoginForm = () => {
const [errors, setErrors] = useState<Record<string, string>>({})
return (
<form onSubmit={handleSubmit} noValidate>
<div className="space-y-4">
{/* Email Field */}
<div>
<label
htmlFor="email"
className="block text-sm font-medium mb-2"
>
Email <span aria-label="required" className="text-error">*</span>
</label>
<input
id="email"
name="email"
type="email"
required
aria-required="true"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? "email-error" : undefined}
className={cn(
"w-full px-3 py-2 border rounded-lg",
errors.email ? "border-error" : "border-input"
)}
/>
{errors.email && (
<p
id="email-error"
role="alert"
className="text-sm text-error mt-1"
>
{errors.email}
</p>
)}
</div>
{/* Password Field */}
<div>
<label
htmlFor="password"
className="block text-sm font-medium mb-2"
>
Password <span aria-label="required" className="text-error">*</span>
</label>
<input
id="password"
name="password"
type="password"
required
aria-required="true"
aria-invalid={!!errors.password}
aria-describedby={errors.password ? "password-error password-requirements" : "password-requirements"}
className={cn(
"w-full px-3 py-2 border rounded-lg",
errors.password ? "border-error" : "border-input"
)}
/>
<p id="password-requirements" className="text-xs text-muted-foreground mt-1">
Password must be at least 12 characters
</p>
{errors.password && (
<p
id="password-error"
role="alert"
className="text-sm text-error mt-1"
>
{errors.password}
</p>
)}
</div>
{/* Submit Button */}
<button
type="submit"
className="w-full bg-primary text-white py-2 px-4 rounded-lg hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
>
Sign In
</button>
</div>
{/* Form-level error */}
{errors.form && (
<div
role="alert"
aria-live="assertive"
className="mt-4 p-3 bg-error-light text-error rounded-lg"
>
{errors.form}
</div>
)}
</form>
)
}
Custom Focus Styles:
/* frontend/src/index.css */
/* Remove default outline and add custom focus ring */
*:focus {
outline: none;
}
*:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
}
/* Button focus styles */
button:focus-visible,
a:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
}
/* Input focus styles */
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
border-color: hsl(var(--ring));
}
/* Skip to content link */
.skip-to-content:focus {
position: absolute;
top: 1rem;
left: 1rem;
z-index: 9999;
padding: 0.75rem 1rem;
background: hsl(var(--primary));
color: hsl(var(--primary-foreground));
border-radius: 0.375rem;
}
Check and Fix Contrast:
// Use colors that meet WCAG AA standards
// ❌ Bad: Low contrast (2.5:1)
<p className="text-gray-400 bg-gray-200">Low contrast text</p>
// ✅ Good: High contrast (4.5:1+)
<p className="text-gray-900 bg-gray-100">High contrast text</p>
// ✅ Good: Using theme colors with proper contrast
<p className="text-foreground bg-background">Theme colors</p>
<button className="bg-primary text-primary-foreground">Button</button>
// For links, ensure visible distinction
<a href="#" className="text-primary underline hover:text-primary/90">
Link text
</a>
Contrast Checker Function:
// frontend/src/utils/colorContrast.ts
export const getContrastRatio = (color1: string, color2: string): number => {
const getLuminance = (color: string) => {
// Convert hex to RGB
const rgb = parseInt(color.slice(1), 16)
const r = (rgb >> 16) & 0xff
const g = (rgb >> 8) & 0xff
const b = (rgb >> 0) & 0xff
// Calculate relative luminance
const [rs, gs, bs] = [r, g, b].map(c => {
c = c / 255
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
})
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs
}
const lum1 = getLuminance(color1)
const lum2 = getLuminance(color2)
const lighter = Math.max(lum1, lum2)
const darker = Math.min(lum1, lum2)
return (lighter + 0.05) / (darker + 0.05)
}
export const meetsWCAGAA = (color1: string, color2: string, isLargeText: boolean = false): boolean => {
const contrast = getContrastRatio(color1, color2)
return isLargeText ? contrast >= 3 : contrast >= 4.5
}
// Usage
console.log(meetsWCAGAA('#0066CC', '#FFFFFF')) // true (7.4:1)
console.log(meetsWCAGAA('#808080', '#FFFFFF')) // false (3.9:1)
// ❌ Bad: Missing alt text
<img src="/team/john.jpg" />
// ✅ Good: Descriptive alt text
<img src="/team/john.jpg" alt="John Doe, Senior QA Engineer" />
// ✅ Decorative images
<img src="/decoration.svg" alt="" aria-hidden="true" />
// ✅ Complex images with longer descriptions
<figure>
<img
src="/chart.png"
alt="Bar chart showing test coverage by module"
aria-describedby="chart-desc"
/>
<figcaption id="chart-desc">
The chart shows test coverage percentages for each module:
Authentication (95%), User Management (88%), Reports (76%),
Settings (92%).
</figcaption>
</figure>
// frontend/src/components/StatusMessage.tsx
export const StatusMessage = ({ message, type }: { message: string; type: 'success' | 'error' | 'info' }) => {
return (
<div
role="status"
aria-live={type === 'error' ? 'assertive' : 'polite'}
aria-atomic="true"
className={cn(
"p-4 rounded-lg",
{
'bg-success-light text-success': type === 'success',
'bg-error-light text-error': type === 'error',
'bg-blue-50 text-blue-900': type === 'info',
}
)}
>
{message}
</div>
)
}
// Usage
<StatusMessage
message="Team member created successfully"
type="success"
/>
// frontend/src/components/admin/AccessibleTable.tsx
export const TeamMembersTable = ({ members }: { members: TeamMember[] }) => {
return (
<table role="table" aria-label="Team members list">
<caption className="sr-only">
List of {members.length} team members
</caption>
<thead>
<tr>
<th scope="col">Photo</th>
<th scope="col">Name</th>
<th scope="col">Role</th>
<th scope="col">Email</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{members.map((member, index) => (
<tr key={member.id}>
<td>
<img
src={member.photo_url}
alt={`${member.name}'s profile photo`}
className="w-12 h-12 rounded-full"
/>
</td>
<th scope="row">{member.name}</th>
<td>{member.role}</td>
<td>{member.email}</td>
<td>
<button
aria-label={`Edit ${member.name}`}
className="mr-2"
>
Edit
</button>
<button
aria-label={`Delete ${member.name}`}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
)
}
/* frontend/src/index.css */
/* Screen reader only class */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
/* Show on focus (for skip links) */
.sr-only:focus {
position: static;
width: auto;
height: auto;
padding: initial;
margin: initial;
overflow: visible;
clip: auto;
white-space: normal;
}
// Usage
<span className="sr-only">Current page</span>
<button aria-label="Close menu">
<X aria-hidden="true" />
<span className="sr-only">Close</span>
</button>
cd frontend
npm install -D @axe-core/playwright
# tests/e2e/test_accessibility.py
from axe_playwright_python import Axe
def test_homepage_accessibility(page):
"""Test homepage accessibility."""
page.goto('http://localhost:5173')
# Run axe accessibility scan
axe = Axe()
results = axe.run(page)
violations = results['violations']
if violations:
print(f"\nFound {len(violations)} accessibility violations:\n")
for violation in violations:
print(f"❌ {violation['id']}: {violation['description']}")
print(f" Impact: {violation['impact']}")
print(f" Help: {violation['helpUrl']}")
print(f" Affected nodes: {len(violation['nodes'])}\n")
# Assert no violations
assert len(violations) == 0, f"Found {len(violations)} accessibility violations"
def test_admin_login_accessibility(page):
"""Test login form accessibility."""
page.goto('http://localhost:5173/admin/login')
axe = Axe()
results = axe.run(page)
assert len(results['violations']) == 0
# tests/e2e/test_keyboard_navigation.py
def test_keyboard_navigation(page):
"""Test keyboard navigation through the page."""
page.goto('http://localhost:5173')
# Start from top
page.keyboard.press('Tab')
# Should focus skip link first
expect(page.locator('.skip-to-content')).to_be_focused()
# Tab through navigation
page.keyboard.press('Tab')
expect(page.locator('nav a:nth-child(1)')).to_be_focused()
# Test Enter key activation
page.keyboard.press('Enter')
# Should navigate
def test_modal_focus_trap(page):
"""Test focus is trapped inside modal."""
page.goto('http://localhost:5173/admin/team-members')
# Open modal
page.click('button:has-text("Add Team Member")')
# Tab through all focusable elements
# Last Tab should cycle back to first element
for _ in range(10):
page.keyboard.press('Tab')
# Focus should still be inside modal
assert page.locator('[role="dialog"]').evaluate('el => el.contains(document.activeElement)')
# Escape should close modal
page.keyboard.press('Escape')
expect(page.locator('[role="dialog"]')).not_to_be_visible()
Test with actual screen readers:
Test checklist:
# Install contrast checker
npm install -D axe-core
# Run contrast check
npx axe http://localhost:5173 --rules=color-contrast
Tools:
Documentation:
✅ WCAG 2.1 AA compliance achieved ✅ All images have descriptive alt text ✅ Semantic HTML used throughout ✅ ARIA labels added to interactive elements ✅ Keyboard navigation fully functional ✅ Focus indicators visible and clear ✅ Color contrast meets 4.5:1 minimum ✅ Forms fully accessible with proper labels ✅ Screen reader tested (VoiceOver/NVDA) ✅ Skip to content link implemented ✅ No accessibility violations found (axe-core) ✅ Automated tests passing
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
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
日本語の概要は準備中です。原文の説明を表示しています。