agentsclimarketplace

Accessibility

Skill MARUCIE/openclaw-foundry/web/public/packs/spellbook-frontend-engineer/skills/accessibility

The curated AI Agent skill marketplace — 37K+ vetted skills, S/A/B/C ratings, deploy anywhere

Install
npx -y skills add MARUCIE/openclaw-foundry --skill accessibility

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when building or reviewing UI components for keyboard and screen reader compatibility, adding ARIA to custom widgets, auditing a page for WCAG AA conformance, or preparing for a formal accessibility review.

SKILL.md

13.5 KB, as published. Nobody here has run it

是什么

Accessibility(无障碍,A11y)是让残障用户与辅助技术(如读屏、键盘、语音)都能顺畅使用产品的工程实践。 用它的效果是:合规风险降低、用户群体扩大、整体交互质量提升(健全用户也受益)。

怎么用

  1. 先用语义化 HTML 表达页面结构,让读屏软件能正确朗读层级与角色。
  2. 为所有交互元素提供键盘可达路径,让不用鼠标的用户也能完成关键流程。
  3. 用 ARIA(Accessible Rich Internet Applications)属性补齐动态组件的语义,让自定义控件不丢失可访问性。
  4. 在颜色与字号上对照 WCAG 标准做对比度检查,让弱视用户也能舒服阅读。
  5. 把无障碍检查接入 CI 自动化,让回归在合并前被发现而不是等用户投诉。

架构图

flowchart LR
  设计稿 --> 语义HTML
  语义HTML --> 键盘可达
  键盘可达 --> ARIA语义
  ARIA语义 --> 对比度校验
  对比度校验 --> CI门禁

Accessibility

Build UIs that work for everyone — keyboard users, screen reader users, users with motor impairments, low-vision users, and users in constrained environments.

When to Activate

  • Building or reviewing UI components (forms, modals, menus, tables)
  • Implementing keyboard navigation or focus management
  • Auditing a page or component for WCAG conformance
  • Adding ARIA attributes to custom interactive widgets
  • Reviewing color palette or typography choices for contrast
  • Writing E2E tests that must be accessible-first
  • Preparing for an accessibility audit or compliance review

WCAG Conformance Levels

LevelWhat it meansTypical target
ABare minimum — removes critical blockersNever ship below this
AAStandard legal/compliance baseline (most regulations)Default target
AAAEnhanced — not always achievable for all contentAim where practical

Most legal requirements (ADA, WCAG 2.1 AA, EN 301 549) map to AA.

Semantic HTML First

Use the right element before reaching for ARIA.

<!-- BAD: div soup -->
<div class="btn" onclick="submit()">Submit</div>
<div class="nav"><div class="nav-item">Home</div></div>
<div class="heading">Page Title</div>

<!-- GOOD: native semantics -->
<button type="submit">Submit</button>
<nav><a href="/">Home</a></nav>
<h1>Page Title</h1>

Landmark Regions

<header>          <!-- banner landmark -->
<nav>             <!-- navigation landmark -->
<main>            <!-- main landmark (one per page) -->
<aside>           <!-- complementary landmark -->
<footer>          <!-- contentinfo landmark -->
<section aria-label="Featured articles">  <!-- region landmark (needs label) -->

Heading Hierarchy

<!-- BAD: skipped levels, visual-only sizing -->
<h1>Site Name</h1>
<h3>Section Title</h3>   <!-- skipped h2 -->
<span class="h2-style">Visual heading, not semantic</span>

<!-- GOOD: sequential, logical outline -->
<h1>Page Title</h1>
  <h2>Section</h2>
    <h3>Subsection</h3>
  <h2>Another Section</h2>

ARIA

Use ARIA only when native HTML cannot express the semantics.

Rule of thumb

1. Use native HTML element                → <button>, <input>, <select>
2. Use native attribute                  → disabled, required, checked
3. Use ARIA role + state + property      → last resort for custom widgets

Common Roles

<!-- Roles that convey widget type -->
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<div role="alertdialog" aria-labelledby="alert-title" aria-describedby="alert-desc">
<div role="tablist"><div role="tab" aria-selected="true"><div role="tabpanel">
<ul role="listbox"><li role="option" aria-selected="false">

<!-- Roles for landmarks (prefer native elements) -->
<div role="banner">     <!-- prefer <header> -->
<div role="main">       <!-- prefer <main> -->
<div role="navigation"> <!-- prefer <nav> -->

States and Properties

<!-- Expandable widgets -->
<button aria-expanded="false" aria-controls="menu-id">Menu</button>
<ul id="menu-id" hidden>...</ul>

<!-- Required and invalid fields -->
<input aria-required="true" aria-invalid="true" aria-describedby="error-id">
<span id="error-id" role="alert">Email is required</span>

<!-- Live regions -->
<div aria-live="polite">   <!-- non-urgent updates (status messages) -->
<div aria-live="assertive" role="alert">  <!-- urgent (errors) -->

<!-- Labelling -->
<button aria-label="Close dialog">✕</button>
<input aria-labelledby="label-id hint-id">  <!-- multiple labels -->
<table aria-describedby="table-desc">

ARIA Anti-Patterns

<!-- BAD: redundant roles -->
<button role="button">      <!-- button already has this role -->
<ul role="list">            <!-- ul already has this role -->
<a href="/" role="link">    <!-- redundant -->

<!-- BAD: presentational role on interactive element -->
<button role="presentation">Submit</button>

<!-- BAD: ARIA label overrides visible text -->
<button aria-label="Click here to proceed">Submit order</button>
<!-- Screen reader says "Click here to proceed" — inconsistent with visual -->

Keyboard Navigation

Focus Order

<!-- Logical DOM order = logical tab order -->
<!-- Avoid tabindex > 0 — it creates a separate, confusing tab sequence -->

<!-- BAD -->
<button tabindex="3">First visually</button>
<button tabindex="1">Second visually</button>

<!-- GOOD: use DOM order, override with tabindex="0" only to add focusability -->
<div tabindex="0" role="button">Custom focusable</div>

<!-- Remove from tab order when visually hidden -->
<div hidden>           <!-- tabindex automatically removed -->
<div aria-hidden="true" tabindex="-1">  <!-- hidden from AT, not in tab order -->

Keyboard Patterns by Widget

WidgetKeys required
ButtonEnter, Space
LinkEnter
CheckboxSpace to toggle
Radio groupArrow keys within group, Tab to leave
Select / ListboxArrow keys, Enter, Escape
Modal dialogTab/Shift+Tab trapped inside, Escape closes
MenuArrow keys, Escape, Enter/Space to select
TabsArrow keys between tabs, Tab into panel
SliderArrow keys, Home, End

Focus Management

// Move focus into dialog when it opens
function openModal(modalEl: HTMLElement) {
  const firstFocusable = modalEl.querySelector<HTMLElement>(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  firstFocusable?.focus();
}

// Trap focus inside modal
function trapFocus(modalEl: HTMLElement, event: KeyboardEvent) {
  if (event.key !== "Tab") return;
  const focusable = modalEl.querySelectorAll<HTMLElement>(
    'button:not([disabled]), [href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const first = focusable[0];
  const last = focusable[focusable.length - 1];

  if (event.shiftKey && document.activeElement === first) {
    last.focus(); event.preventDefault();
  } else if (!event.shiftKey && document.activeElement === last) {
    first.focus(); event.preventDefault();
  }
}

// Return focus to trigger when dialog closes
function closeModal(triggerEl: HTMLElement) {
  triggerEl.focus();
}

Color and Visual Design

Contrast Ratios (WCAG AA)

TextMinimum ratioEnhanced (AAA)
Normal text (< 18pt / 14pt bold)4.5:17:1
Large text (≥ 18pt / 14pt bold)3:14.5:1
UI components, icons, graphical elements3:1
Decorative contentNo requirement
/* BAD: low contrast */
color: #999;          /* #999 on white = 2.85:1 */
color: #767676;       /* borderline 4.54:1 — just passes but risky */

/* GOOD: reliable contrast */
color: #595959;       /* 7.0:1 on white */
color: #0066cc;       /* 5.74:1 on white — accessible blue */

Don't Rely on Color Alone

<!-- BAD: color-only status -->
<span class="status-dot green"></span>

<!-- GOOD: color + text/icon -->
<span class="status-dot green" aria-label="Active"></span>Active

<!-- BAD: error indicated only by red border -->
<input class="error-border">

<!-- GOOD: error icon + text + aria-invalid -->
<input aria-invalid="true" aria-describedby="err">
<span id="err">⚠ Email is required</span>

Motion and Animation

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Forms

<!-- BAD: placeholder as label -->
<input type="email" placeholder="Enter email">

<!-- GOOD: explicit label, associated by for/id -->
<label for="email">Email address</label>
<input id="email" type="email" autocomplete="email"
       aria-required="true" aria-describedby="email-hint">
<span id="email-hint">We'll only use this to send your receipt.</span>

<!-- Group related fields -->
<fieldset>
  <legend>Shipping address</legend>
  <label for="street">Street</label>
  <input id="street" type="text" autocomplete="street-address">
</fieldset>

<!-- Error association -->
<label for="phone">Phone number</label>
<input id="phone" type="tel" aria-invalid="true" aria-describedby="phone-error">
<span id="phone-error" role="alert">Enter a valid 10-digit phone number</span>

Images and Media

<!-- Informative image: describe the content -->
<img src="chart.png" alt="Bar chart showing Q4 revenue up 23% vs Q3">

<!-- Decorative image: empty alt suppresses announcement -->
<img src="divider.png" alt="">

<!-- Icon button: label the action, not the icon -->
<button aria-label="Delete item"><svg aria-hidden="true">...</svg></button>

<!-- Complex image: long description -->
<figure>
  <img src="org-chart.png" alt="Organisation chart — see table below for text version">
  <figcaption><!-- or link to detailed description --></figcaption>
</figure>

<!-- Video: captions + audio description -->
<video>
  <track kind="captions" src="captions-en.vtt" srclang="en" label="English" default>
  <track kind="descriptions" src="descriptions.vtt" srclang="en" label="Audio descriptions">
</video>

Testing

Manual checklist (do for every component)

  1. Tab through the page — every interactive element reachable?
  2. Nothing requires a mouse — all actions completable by keyboard?
  3. Focus indicator always visible?
  4. Test with screen reader (NVDA/Firefox on Windows, VoiceOver/Safari on Mac)

Automated tools

# Axe CLI
npx axe http://localhost:3000 --reporter cli

# Pa11y
npx pa11y http://localhost:3000

# Playwright + axe-core
npm install @axe-core/playwright
// Playwright accessibility scan
import { checkA11y, injectAxe } from "axe-playwright";

test("home page has no accessibility violations", async ({ page }) => {
  await page.goto("/");
  await injectAxe(page);
  await checkA11y(page, null, {
    detailedReport: true,
    detailedReportOptions: { html: true },
  });
});

Contrast check

# Check contrast via CLI
npx contrast-ratio "#595959" "#ffffff"

# Or use browser DevTools: Accessibility panel → Contrast ratio

Red Flags

  • aria-label on every element — ARIA overrides native semantics; use semantic HTML first and add ARIA only when native elements can't express the required role or state
  • role="button" on a <div> or <span> — custom button roles require manually implementing keyboard behavior; use <button> and get focus, Enter, and Space for free
  • alt="" on informational images — empty alt hides the image from screen readers entirely; empty alt is only correct for purely decorative images that convey no content
  • placeholder as the only label — placeholder text disappears on focus and has insufficient contrast ratio; every input must have an associated visible <label> element
  • outline: none without a visible replacement — removing the default focus ring makes keyboard navigation invisible; always provide a visible focus indicator that meets 3:1 contrast
  • Automated scan as the complete accessibility test — axe/pa11y catches ~30% of WCAG issues; focus order, reading order, and screen reader announcements require manual verification
  • Modal that doesn't trap focus — focus that escapes an open modal to background content disorients screen reader users; implement a focus trap and return focus to the trigger element on close

Checklist

  • All interactive elements reachable and operable by keyboard alone
  • Focus order follows logical reading order — no tabindex > 0
  • Focus indicator visible at all times (no outline: none without replacement)
  • Custom widgets implement correct ARIA role, state, and keyboard pattern
  • Every <img> has alt — descriptive for content, empty for decorative
  • All form inputs have associated <label> — not placeholder only
  • Error messages programmatically associated with their input (aria-describedby)
  • Color contrast ≥ 4.5:1 for normal text, ≥ 3:1 for large text and UI components
  • No information conveyed by color alone
  • prefers-reduced-motion respected for animations
  • Modal dialogs trap focus and return it to trigger on close
  • Page has a single <main>, logical heading hierarchy starting at h1
  • Automated scan (axe/pa11y) passes with zero violations

See also: solution-testing, coding-standards

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.