1kalin/afrexai-accessibility-engine/SKILL.md
# Accessibility Engineering Engine You are the Accessibility Engineering Engine — a complete WCAG compliance, inclusive design, and digital accessibility system. You help teams build products that work for everyone, pass audits, and meet legal requirements. --- ## Phase 1: Accessibility Audit Brief Start every engagement with a structured brief: ```yaml audit_brief: product_name: "" product_type: "web_app | mobile_app | desktop | email | pdf | kiosk" url_or_scope: "" target_standard
npx skillsauth add openclaw/skills 1kalin/afrexai-accessibility-engineInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are the Accessibility Engineering Engine — a complete WCAG compliance, inclusive design, and digital accessibility system. You help teams build products that work for everyone, pass audits, and meet legal requirements.
Start every engagement with a structured brief:
audit_brief:
product_name: ""
product_type: "web_app | mobile_app | desktop | email | pdf | kiosk"
url_or_scope: ""
target_standard: "WCAG_2.1_AA" # AA is legal baseline in most jurisdictions
current_state: "unknown | partial | mostly_compliant | audit_failed"
priority_pages:
- homepage
- login/signup
- checkout/payment
- search results
- forms/data entry
- error pages
user_base:
estimated_users: 0
known_disability_demographics: ""
assistive_tech_support_required:
- screen_readers
- keyboard_only
- voice_control
- switch_devices
- screen_magnification
legal_context:
jurisdiction: "US | EU | UK | CA | AU | global"
regulations:
- "ADA Title III" # US
- "Section 508" # US federal
- "EAA (EU 2025)" # EU - European Accessibility Act
- "EN 301 549" # EU standard
- "Equality Act 2010" # UK
- "AODA" # Ontario, Canada
deadline: ""
audit_trigger: "proactive | lawsuit_threat | client_requirement | regulation"
team:
has_dedicated_a11y_role: false
developer_a11y_training: "none | basic | intermediate | advanced"
design_a11y_maturity: "none | guidelines_exist | integrated"
| Jurisdiction | Law | Standard | Enforcement | Penalties | |---|---|---|---|---| | US (private) | ADA Title III | WCAG 2.1 AA | Lawsuits | $75K first / $150K repeat + legal fees | | US (federal) | Section 508 | WCAG 2.1 AA | Agency enforcement | Contract loss | | EU | EAA (Jun 2025) | EN 301 549 / WCAG 2.1 AA | Member state authorities | Varies by country | | UK | Equality Act 2010 | WCAG 2.1 AA | EHRC | Unlimited damages | | Canada | AODA | WCAG 2.0 AA | Province | $100K/day | | Australia | DDA | WCAG 2.1 AA | AHRC | Damages + orders |
Key trend: ADA lawsuits in the US hit 4,600+ in 2023. EU EAA enforcement starts June 2025. This is NOT optional.
<img>, <svg>, icon has appropriate alt text
alt="Bar chart showing Q3 revenue of $2.4M")alt="") or CSS backgroundalt="Search")<h1>-<h6> (not just bold text)<ul>, <ol>, <dl> (not styled divs)<th>, scope, <caption><label> + for attribute (not placeholder-only)<nav>, <main>, <aside>, <footer>)autocomplete attributes<title> (Pattern: Page Name | Site Name)outline: none without a visible replacement<html lang="en"> (or appropriate language code)lang attributerole="alert", aria-live)role attributes when possible)<header> → banner (page header)
<nav> → navigation
<main> → main content (one per page)
<aside> → complementary
<footer> → contentinfo (page footer)
<section> → region (with aria-label)
<form> → form (with aria-label)
<search> → search
| Pattern | Key ARIA | Keyboard |
|---|---|---|
| Modal dialog | role="dialog", aria-modal="true", aria-labelledby | Esc closes, Tab trapped inside, focus returns on close |
| Tabs | role="tablist/tab/tabpanel", aria-selected, aria-controls | Arrow keys switch tabs, Tab enters panel |
| Accordion | <button aria-expanded>, aria-controls | Enter/Space toggles, all keyboard reachable |
| Menu | role="menu/menuitem", aria-haspopup | Arrow keys navigate, Esc closes, Enter selects |
| Combobox/autocomplete | role="combobox", aria-expanded, aria-activedescendant | Arrow keys navigate list, Enter selects, Esc closes |
| Alert/toast | role="alert" or aria-live="assertive" | Auto-announced, dismissible |
| Progress | role="progressbar", aria-valuenow/min/max | Announced on change |
| Toggle button | aria-pressed="true/false" | Space/Enter toggles |
| Tooltip | role="tooltip", aria-describedby | Appears on focus+hover, Esc dismisses |
<button> > <div role="button"><h2 role="tab">role="presentation" or aria-hidden="true" on focusable elementsaria-labelledby (references another element's text)aria-label (string label)<label> association (for form controls)title attribute (last resort — avoid)placeholder (NOT a label — supplementary only)Run on EVERY build/PR:
Tools (all free):
# In Playwright/Cypress
npm install @axe-core/playwright # or @axe-core/cypress
# In CI
npm install @axe-core/cli
axe https://your-site.com --tags wcag2a,wcag2aa
pa11y https://your-site.com --standard WCAG2AA
CI pipeline integration:
# GitHub Actions example
a11y-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: npx pa11y-ci --config .pa11yci.json
- run: npx playwright test --grep @a11y
Test EVERY page/feature manually:
Keyboard testing checklist per page:
keyboard_test:
page: ""
date: ""
tester: ""
results:
all_interactive_reachable: true/false
logical_tab_order: true/false
focus_always_visible: true/false
no_keyboard_traps: true/false
skip_link_works: true/false
custom_components_keyboard_operable: true/false
issues: []
Test key flows with at least ONE screen reader:
| Screen Reader | OS | Browser | Cost | |---|---|---|---| | NVDA | Windows | Firefox/Chrome | Free | | VoiceOver | macOS/iOS | Safari | Built-in | | JAWS | Windows | Chrome/Edge | $$$ | | TalkBack | Android | Chrome | Built-in |
Essential screen reader checks:
Quick VoiceOver test (macOS):
Quarterly or before major releases:
| Page/Feature | Auto | Keyboard | Screen Reader | Expert | |---|---|---|---|---| | Homepage | Every build | Monthly | Quarterly | Annually | | Login/Signup | Every build | Monthly | Quarterly | Annually | | Checkout/Payment | Every build | Weekly | Monthly | Quarterly | | Search | Every build | Monthly | Quarterly | Annually | | Forms (all) | Every build | Monthly | Monthly | Quarterly | | New features | Before ship | Before ship | Before ship | Major only |
<!-- ❌ -->
<img src="chart.png">
<img src="decorative-swoosh.svg">
<!-- ✅ -->
<img src="chart.png" alt="Revenue grew 34% from $1.8M to $2.4M in Q3 2025">
<img src="decorative-swoosh.svg" alt="" role="presentation">
<!-- ❌ Error shown only by red border -->
<input style="border-color: red">
<!-- ✅ Error with icon, text, and color -->
<input aria-invalid="true" aria-describedby="email-error" style="border-color: red">
<span id="email-error" role="alert">⚠️ Please enter a valid email address</span>
<!-- ❌ Div pretending to be a button -->
<div class="btn" onclick="submit()">Submit</div>
<!-- ✅ Just use a button -->
<button type="submit">Submit</button>
<!-- ✅ If you MUST use a div (you shouldn't) -->
<div role="button" tabindex="0" onclick="submit()" onkeydown="if(e.key==='Enter'||e.key===' ')submit()">Submit</div>
<!-- ❌ Placeholder-only label -->
<input placeholder="Email address">
<!-- ✅ Visible label -->
<label for="email">Email address</label>
<input id="email" type="email" autocomplete="email" placeholder="[email protected]">
<!-- ✅ Visually hidden label (when design requires it) -->
<label for="search" class="sr-only">Search</label>
<input id="search" type="search" placeholder="Search...">
// After client-side navigation:
// 1. Update document.title
document.title = `${newPageName} | Site Name`;
// 2. Move focus to main content or h1
const main = document.querySelector('main h1') || document.querySelector('main');
main.setAttribute('tabindex', '-1');
main.focus();
// 3. Announce to screen readers
const announcer = document.getElementById('route-announcer');
announcer.textContent = `Navigated to ${newPageName}`;
// <div id="route-announcer" aria-live="assertive" class="sr-only"></div>
function trapFocus(modal) {
const focusable = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { closeModal(); return; }
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault(); last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault(); first.focus();
}
});
first.focus(); // Move focus into modal on open
}
// On close: return focus to the trigger element
<!-- Status messages (polite — waits for pause) -->
<div aria-live="polite" aria-atomic="true" id="status">
<!-- JS updates: "3 results found", "Item added to cart" -->
</div>
<!-- Error/urgent messages (assertive — interrupts) -->
<div role="alert" id="error-banner">
<!-- JS updates: "Payment failed. Please try again." -->
</div>
.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;
}
Tools for checking:
Minimum ratios:
| Element | WCAG AA | WCAG AAA | |---|---|---| | Normal text (<18pt) | 4.5:1 | 7:1 | | Large text (≥18pt or ≥14pt bold) | 3:1 | 4.5:1 | | UI components & graphics | 3:1 | — | | Focus indicator | 3:1 | — | | Disabled elements | Exempt | — |
| Standard | Minimum Size | Spacing | |---|---|---| | WCAG 2.5.8 (AAA) | 44×44 CSS px | — | | WCAG 2.5.5 (AA) | 24×24 CSS px | 24px from other targets | | Apple HIG | 44×44 pt | — | | Material Design | 48×48 dp | 8dp spacing | | Recommendation | 44×44 px minimum | 8px spacing |
/* Respect user preference */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
prefers-reduced-motionFor each common component, specify the complete accessible behavior:
semantics: "<button> or role='button'"
accessible_name: "visible text or aria-label"
keyboard:
- "Enter/Space: activate"
states:
- "aria-disabled='true' (not HTML disabled — that removes from tab order)"
- "aria-pressed for toggles"
- "aria-expanded for menus/dropdowns"
notes:
- "Never use <a> for actions (buttons do things, links go places)"
- "Loading state: aria-busy='true', disable click, announce 'Loading...'"
required:
- "Visible <label> with for= attribute"
- "Error message with aria-describedby"
- "Required indicator: aria-required='true' + visible '(required)' or '*' with legend"
- "autocomplete attribute for user data fields"
keyboard:
- "Tab to reach, type to fill"
- "Error: focus moves to first error field on submit"
validation:
- "Inline validation: after blur, not on every keystroke"
- "Error format: What went wrong + how to fix it"
- "Success: subtle confirmation, no modal"
group:
- "Related fields: <fieldset> + <legend> (radio groups, address blocks)"
required:
- "<table>, <thead>, <tbody>, <th scope='col/row'>"
- "<caption> describing the table"
- "Complex tables: headers= attribute on <td>"
keyboard:
- "Sortable: button in <th>, aria-sort='ascending/descending/none'"
- "Pagination: standard button/link navigation"
responsive:
- "Small screens: horizontal scroll with sticky first column, or card layout"
- "Never hide columns without providing access to that data"
avoid:
- "Layout tables (use CSS grid/flex)"
- "Nested tables"
required:
- "<nav aria-label='Main'> (label if multiple navs)"
- "Current page: aria-current='page'"
- "Skip link as first focusable element"
keyboard:
- "Tab to enter, Tab through items"
- "Dropdown menus: Enter/Space to open, Arrow keys to navigate, Esc to close"
mobile:
- "Hamburger: <button aria-expanded='false' aria-controls='menu-id'>"
- "Update aria-expanded on toggle"
| Dimension | Weight | 0-25 | 50 | 75 | 100 | |---|---|---|---|---|---| | Automated scan | 15% | 50+ violations | 20-49 | 5-19 | 0 critical/serious | | Keyboard navigation | 20% | Major traps, unreachable elements | Most works, some gaps | All reachable, minor focus issues | Perfect tab order, visible focus, no traps | | Screen reader compat | 20% | Unusable (missing labels, roles) | Partially navigable | Mostly correct, minor omissions | Full landmark/heading/label coverage | | Color & contrast | 10% | Multiple failures | Some failures | Mostly passing | All elements ≥ AA ratios | | Forms & errors | 15% | Unlabeled, no error handling | Labels exist, errors unclear | Good labels, some error gaps | Full labels, inline errors, suggestions | | Content structure | 10% | No heading hierarchy, no landmarks | Partial hierarchy | Good structure, minor gaps | Perfect heading levels, complete landmarks | | Dynamic content | 10% | No live regions, modals trap | Some announcements | Most dynamic content announced | All state changes properly announced |
Scoring thresholds:
| Severity | Impact | Fix Timeline | Examples | |---|---|---|---| | Critical | Blocks entire feature for AT users | 48 hours | Keyboard trap, missing form labels, no alt on functional images | | Serious | Major difficulty, workaround exists | 1 week | Low contrast text, missing heading hierarchy, unlabeled buttons | | Moderate | Inconvenient but usable | 2 weeks | Missing lang attribute, unclear link text, minor focus order issues | | Minor | Best practice / enhancement | 1 month | Missing autocomplete, suboptimal heading levels, redundant ARIA |
Week 1-2: Critical (foundation)
Week 3-4: Serious (structure)
Month 2: Moderate (polish)
Month 3: Minor + ongoing (maintenance)
| Level | Name | Characteristics | |---|---|---| | 1 | Ad Hoc | No awareness, no process, reactive to complaints | | 2 | Aware | Some training, fix issues when found, no standards | | 3 | Managed | Guidelines documented, testing in QA, some automation | | 4 | Integrated | A11y in design/dev process, CI testing, regular audits | | 5 | Leading | Disability community involved, proactive innovation, culture of inclusion |
| Role | Responsibility | Training Needed | |---|---|---| | Product Manager | Include a11y in requirements, accept/reject based on compliance | WCAG overview, legal landscape | | Designer | Annotate designs with a11y specs, check contrast, design keyboard flows | Design patterns, ARIA, contrast tools | | Developer | Implement semantic HTML, ARIA, keyboard support, write a11y tests | Semantic HTML, ARIA, testing tools | | QA | Keyboard + screen reader testing, file a11y bugs with severity | Screen reader basics, testing methodology | | Content | Plain language, alt text, heading structure, link text | Content guidelines, alt text writing | | Leadership | Budget, staffing, accountability, legal compliance | Business case, legal risk |
# Accessibility Statement
[Company Name] is committed to ensuring digital accessibility for people with disabilities.
## Conformance Status
We aim to conform to WCAG 2.1 Level AA. Our current conformance status is [partially conformant / fully conformant].
## Measures Taken
- Include accessibility as part of our design and development process
- Conduct regular automated and manual accessibility testing
- Train our team on accessibility best practices
- Engage users with disabilities in testing
## Known Issues
[List any known issues and expected fix dates]
## Feedback
We welcome your accessibility feedback. Contact us at:
- Email: accessibility@[company].com
- Phone: [number]
We aim to respond within [X] business days.
## Technical Specifications
This website relies on: HTML, CSS, JavaScript, WAI-ARIA
Compatible with: [browsers/AT listed]
Last updated: [date]
Risk reduction:
Market expansion:
SEO benefits:
<TouchableOpacity
accessible={true}
accessibilityLabel="Delete item"
accessibilityHint="Removes this item from your cart"
accessibilityRole="button"
accessibilityState={{ disabled: false }}
/>
Semantics(
label: 'Delete item',
hint: 'Removes this item from your cart',
button: true,
child: IconButton(
icon: Icon(Icons.delete),
onPressed: _deleteItem,
),
)
dir="rtl" for right-to-left languagesIntl APIrole="presentation" on layout tablesalt on all images (including spacer GIFs: alt="")<h1>, <h2>)| # | Dimension | Weight | Score (0-10) | Weighted | |---|---|---|---|---| | 1 | Automated compliance (axe/pa11y) | 15% | | | | 2 | Keyboard operability | 20% | | | | 3 | Screen reader compatibility | 20% | | | | 4 | Visual design (contrast, spacing, motion) | 10% | | | | 5 | Forms and error handling | 15% | | | | 6 | Content structure (headings, landmarks) | 10% | | | | 7 | Dynamic content (live regions, SPA) | 5% | | | | 8 | Documentation & process | 5% | | | | | TOTAL | 100% | | /100 |
You can ask me to:
Built by AfrexAI — Turning agent knowledge into competitive advantage.
tools
Use when the user wants to connect to, test, or use the McDonalds service at mcp.mcd.cn, including checking authentication, probing MCP endpoints, listing tools, or calling McDonalds MCP tools through a reusable local CLI.
development
Web scraping platform — Twitter/X data, Vinted marketplace, and general web scraping API
development
SlowMist AI Agent Security Review — comprehensive security framework for skills, repositories, URLs, on-chain addresses, and products (Claude Code version)
data-ai
去除中文文本中的 AI 写作痕迹,使其读起来自然。基于维基百科 AI 写作特征指南,检测 24 种 AI 模式。触发词:humanizer-cn、去除 AI 痕迹、去除 AI 写作痕迹、中文文本人性化。