Token导航 LogoToken导航TokenDH.com
前端设计操作浏览器github未标认证来源可访问clear审计通过

form-accessibility表单可访问性

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

1,058

周安装

45

GitHub Stars

8

下载量

371
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:form-accessibility(表单可访问性)
来源仓库:https://github.com/bbeierle12/skill-mcp-claude
仓库路径:skills/form-accessibility
安装命令:
npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill form-accessibility
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/bbeierle12/skill-mcp-claude --skill form-accessibility

简介

form-accessibility 用于辅助前端页面、组件、样式和交互逻辑的开发与维护,提升可访问性。

  • 适用于 React、Next.js、Vue、Tailwind、CSS 等项目中的表单组件开发与审查。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装,需结合项目现有设计系统和构建方式使用。
  • 涉及页面改动时应配合本地预览和构建检查确认视觉效果,避免生成孤立片段。
  • 该技能归类为前端设计类,当前维护状态尚不明确,建议进一步核实稳定性。

SKILL.md

Form Accessibility

WCAG 2.2 AA compliance patterns for forms. Ensures forms work for keyboard users, screen reader users, and users with cognitive or motor disabilities.

Quick Start

// Accessible form field pattern
<div className="form-field">
  {/* 1. Visible label (never placeholder-only) */}
  <label htmlFor="email">
    Email
    <span className="required" aria-hidden="true">*</span>
  </label>

  {/* 2. Hint text (separate from label) */}
  <span id="email-hint" className="hint">
    We'll send your confirmation here
  </span>

  {/* 3. Input with full ARIA binding */}
  <input
    id="email"
    type="email"
    autoComplete="email"
    aria-required="true"
    aria-invalid={hasError}
    aria-describedby={hasError ? "email-error email-hint" : "email-hint"}
  />

  {/* 4. Error message (announced by screen readers) */}
  {hasError && (
    <span id="email-error" className="error" role="alert">
      Please enter a valid email address
    </span>
  )}
</div>

WCAG 2.2 Form Requirements

Critical Criteria

CriterionLevelRequirementImplementation
1.3.1 Info & RelationshipsAStructure conveyed programmatically<label>, <fieldset>, aria-describedby
1.3.5 Identify Input PurposeAAInput purpose identifiableautocomplete attributes
2.1.1 KeyboardAAll functionality via keyboardTab order, focus management
2.4.6 Headings & LabelsAALabels describe purposeDescriptive, visible labels
2.4.11 Focus Not ObscuredAAFocus not hidden by other contentScroll behavior, sticky elements
2.5.8 Target SizeAA24×24px minimum touch targetButton/input sizing
3.3.1 Error IdentificationAErrors identified and describedaria-invalid, error messages
3.3.2 Labels or InstructionsALabels providedVisible labels, not just placeholders
3.3.3 Error SuggestionAASuggestions for fixing errorsActionable error messages
3.3.7 Redundant EntryADon't re-ask for info already providedForm state management
3.3.8 Accessible AuthenticationAANo cognitive function testsNo CAPTCHAs requiring text recognition

New in WCAG 2.2 (October 2023)

2.4.11 Focus Not Obscured (AA)

/* Ensure focus is never hidden by sticky headers */
.sticky-header {
  position: sticky;
  top: 0;
}

input:focus {
  /* Browser should scroll input into view above sticky elements */
  scroll-margin-top: 80px; /* Height of sticky header */
}

2.5.8 Target Size (AA)

/* Minimum 24×24px touch targets */
button,
input[type="submit"],
input[type="checkbox"],
input[type="radio"] {
  min-width: 24px;
  min-height: 24px;
}

/* Better: 44×44px for comfortable touch */
.touch-friendly {
  min-width: 44px;
  min-height: 44px;
}

3.3.7 Redundant Entry (A)

// ❌ BAD: Asking for email twice
<input name="email" />
<input name="confirmEmail" />

// ✅ GOOD: Ask once, show confirmation
<input name="email" />
<p>Confirmation will be sent to: {email}</p>

3.3.8 Accessible Authentication (AA)

// ❌ BAD: CAPTCHA requiring text recognition
<img src="captcha.png" alt="Enter the text shown" />

// ✅ GOOD: Alternative verification methods
<button type="button" onClick={sendVerificationEmail}>
  Send verification code to email
</button>

ARIA Patterns

Error Message Binding

// Pattern: aria-describedby links input to error
<input
  id="email"
  aria-invalid={hasError ? "true" : "false"}
  aria-describedby={hasError ? "email-error" : undefined}
/>

{hasError && (
  <span id="email-error" role="alert">
    {errorMessage}
  </span>
)}

Multiple Descriptions

// Pattern: Combine hint + error in aria-describedby
<input
  id="password"
  aria-describedby={[
    "password-hint",
    hasError && "password-error"
  ].filter(Boolean).join(" ")}
/>

<span id="password-hint">Must be at least 8 characters</span>
{hasError && <span id="password-error" role="alert">{error}</span>}

Required Fields

// Pattern: Announce required status
<label htmlFor="name">
  Name
  <span className="required" aria-hidden="true">*</span>
  {/* Visual indicator hidden from SR, aria-required announces it */}
</label>

<input
  id="name"
  aria-required="true"
/>

// Alternative: Required in label (simpler)
<label htmlFor="name">Name (required)</label>
<input id="name" required />

Field Groups

// Pattern: fieldset + legend for related fields
<fieldset>
  <legend>Shipping Address</legend>

  <label htmlFor="street">Street</label>
  <input id="street" autoComplete="street-address" />

  <label htmlFor="city">City</label>
  <input id="city" autoComplete="address-level2" />
</fieldset>

Radio/Checkbox Groups

// Pattern: fieldset groups options, legend is the question
<fieldset>
  <legend>Preferred contact method</legend>

  <label>
    <input type="radio" name="contact" value="email" />
    Email
  </label>

  <label>
    <input type="radio" name="contact" value="phone" />
    Phone
  </label>
</fieldset>

Focus Management

Focus on First Error

// On form submit with errors, focus first invalid field
function handleSubmit(e: FormEvent) {
  e.preventDefault();

  const firstError = formRef.current?.querySelector('[aria-invalid="true"]');
  if (firstError) {
    (firstError as HTMLElement).focus();
    return;
  }

  // Submit if valid
  submitForm();
}

Focus on Step Change (Multi-step)

// Move focus to step heading when changing steps
function goToStep(stepNumber: number) {
  setCurrentStep(stepNumber);

  // Wait for render, then focus
  requestAnimationFrame(() => {
    const heading = document.getElementById(`step-${stepNumber}-heading`);
    heading?.focus();
  });
}

// Heading must be focusable
<h2 id="step-2-heading" tabIndex={-1}>Shipping Address</h2>

Skip Links

// Allow skipping to form
<a href="#main-form" className="skip-link">
  Skip to form
</a>

<form id="main-form">
  {/* Form content */}
</form>

// CSS for skip link
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}

Focus Trap (Modals)

// Keep focus within modal form
function FocusTrap({ children }) {
  const trapRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const trap = trapRef.current;
    if (!trap) return;

    const focusableElements = trap.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );
    const firstElement = focusableElements[0] as HTMLElement;
    const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;

    function handleKeyDown(e: KeyboardEvent) {
      if (e.key !== 'Tab') return;

      if (e.shiftKey && document.activeElement === firstElement) {
        e.preventDefault();
        lastElement.focus();
      } else if (!e.shiftKey && document.activeElement === lastElement) {
        e.preventDefault();
        firstElement.focus();
      }
    }

    trap.addEventListener('keydown', handleKeyDown);
    firstElement?.focus();

    return () => trap.removeEventListener('keydown', handleKeyDown);
  }, []);

  return <div ref={trapRef}>{children}</div>;
}

Color & Contrast

Error States (Colorblind-Safe)

/* ❌ BAD: Color only */
.error {
  border-color: red;
}

/* ✅ GOOD: Color + icon + text */
.field-error {
  border-color: #dc2626;
  border-width: 2px;
}

.field-error::after {
  content: "";
  background-image: url("data:image/svg+xml,..."); /* Error icon */
}

.error-message {
  color: #dc2626;
  font-weight: 500;
}

.error-message::before {
  content: "⚠ "; /* Text indicator */
}

Focus Indicators

/* Focus must have 3:1 contrast ratio */
input:focus {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

/* For dark backgrounds */
input:focus {
  outline: 2px solid #60a5fa;
  outline-offset: 2px;
}

/* Never remove outline without replacement */
/* ❌ BAD */
input:focus {
  outline: none;
}

/* ✅ GOOD: Custom focus style */
input:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.5);
}

Validation States (Colorblind-Friendly)

// Use icons + text, not just color
function ValidationIndicator({ state }: { state: 'valid' | 'invalid' | 'idle' }) {
  if (state === 'idle') return null;

  return (
    <span className={`indicator ${state}`} aria-hidden="true">
      {state === 'valid' && '✓'}
      {state === 'invalid' && '✗'}
    </span>
  );
}

Keyboard Navigation

Tab Order

// Natural tab order (no positive tabindex needed)
// ❌ BAD: Manual tab order
<input tabIndex={2} />
<input tabIndex={1} />
<input tabIndex={3} />

// ✅ GOOD: Natural DOM order
<input /> {/* tabIndex implicitly 0 */}
<input />
<input />

Escape Key Handling

// Allow Escape to close dropdowns, cancel modals
function Modal({ onClose, children }) {
  useEffect(() => {
    function handleEscape(e: KeyboardEvent) {
      if (e.key === 'Escape') {
        onClose();
      }
    }

    document.addEventListener('keydown', handleEscape);
    return () => document.removeEventListener('keydown', handleEscape);
  }, [onClose]);

  return <div role="dialog" aria-modal="true">{children}</div>;
}

Enter to Submit

// Forms submit on Enter by default
// For buttons that shouldn't submit:
<button type="button" onClick={handleAction}>
  Add Item
</button>

// For preventing Enter submit on specific fields:
<input
  onKeyDown={(e) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      // Do something else
    }
  }}
/>

Live Regions

Error Announcements

// Announce errors when they appear
<div aria-live="polite" aria-atomic="true" className="sr-only">
  {errorCount > 0 && `${errorCount} errors in form`}
</div>

// Or use role="alert" for immediate announcement
{hasError && (
  <span role="alert">{errorMessage}</span>
)}

Loading States

// Announce loading state
<button type="submit" disabled={isLoading}>
  {isLoading ? (
    <>
      <span aria-hidden="true">Loading...</span>
      <span className="sr-only">Submitting form, please wait</span>
    </>
  ) : (
    'Submit'
  )}
</button>

// Or use aria-busy
<form aria-busy={isLoading}>
  {/* ... */}
</form>

Success Messages

// Announce successful submission
{isSuccess && (
  <div role="status" aria-live="polite">
    Form submitted successfully!
  </div>
)}

Screen Reader Only Content

/* Visually hidden but announced by screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Allow focus for skip links */
.sr-only-focusable:focus {
  position: static;
  width: auto;
  height: auto;
  overflow: visible;
  clip: auto;
  white-space: normal;
}

Testing Accessibility

Automated Tools

# axe-core (recommended)
npm install @axe-core/react

# In development
import React from 'react';
import ReactDOM from 'react-dom';
import axe from '@axe-core/react';

if (process.env.NODE_ENV !== 'production') {
  axe(React, ReactDOM, 1000);
}

Manual Testing Checklist

  1. Keyboard only: Can you complete the form using only Tab, Enter, Space, and Arrow keys?
  2. Screen reader: Does VoiceOver/NVDA announce labels, errors, and required status?
  3. Zoom 200%: Is the form usable at 200% browser zoom?
  4. High contrast: Is everything visible in Windows High Contrast mode?
  5. Focus visible: Can you always see which element is focused?

Testing Script

// Automated accessibility test
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('form is accessible', async () => {
  const { container } = render(<LoginForm />);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

test('error state is accessible', async () => {
  const { container } = render(<LoginForm />);

  // Trigger error
  fireEvent.blur(screen.getByLabelText(/email/i));

  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

File Structure

form-accessibility/
├── SKILL.md
├── references/
│   ├── wcag-2.2-forms.md       # Full WCAG criteria breakdown
│   └── aria-patterns.md        # Complete ARIA reference
└── scripts/
    ├── aria-form-wrapper.tsx   # Automatic ARIA binding
    ├── focus-manager.ts        # Focus trap, error focus
    ├── error-announcer.ts      # Live region management
    └── accessibility-validator.ts  # Runtime a11y checks

Reference

  • references/wcag-2.2-forms.md — Complete WCAG 2.2 criteria for forms
  • references/aria-patterns.md — Detailed ARIA implementation patterns

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

30.76%
按下载量换算114

OpenCode

24.39%
按下载量换算90

Antigravity

20.28%
按下载量换算75

windsurf

11.57%
按下载量换算43

Gemini CLI

7.72%
按下载量换算29

Codex

3.56%
按下载量换算13

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills