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

web-accessibility网络可访问性

Agent Skill

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

总安装

310,896

周安装

12,735

GitHub Stars

88

下载量

100,584
CodexClaudeCursorGemini CLI

安装说明

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

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

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

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

命令行安装

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

skills.shnpx skills
npx skills add https://github.com/supercent-io/skills-template --skill web-accessibility

简介

针对语义 HTML、键盘导航、ARIA 属性和屏幕阅读器支持实施 WCAG 2.1 辅助功能标准。

  • 涵盖语义 HTML 结构、键盘导航(Tab、箭头键、ESC)以及模态和交互组件的焦点管理
  • 提供 ARIA 属性指南(aria-label、aria-live、aria-hidden、角色属性)以及实用的 React/TypeScript 示例
  • 包括颜色对比度要求 (WCAG AA/AAA)、视觉焦点指示器以及使用 axe-core 和 Lighthouse 的测试策略
  • 强制执行强制性规则:仅键盘可用性、图像替代文本、关联表单标签以及禁止删除轮廓

SKILL.md

Web Accessibility (A11y)

When to use this skill

  • New UI Component Development: Designing accessible components
  • Accessibility Audit: Identifying and fixing accessibility issues in existing sites
  • Form Implementation: Writing screen reader-friendly forms
  • Modals/Dropdowns: Focus management and keyboard trap prevention
  • WCAG Compliance: Meeting legal requirements or standards

Input Format

Required Information

  • Framework: React, Vue, Svelte, Vanilla JS, etc.
  • Component Type: Button, Form, Modal, Dropdown, Navigation, etc.
  • WCAG Level: A, AA, AAA (default: AA)

Optional Information

  • Screen Reader: NVDA, JAWS, VoiceOver (for testing)
  • Automated Testing Tool: axe-core, Pa11y, Lighthouse (default: axe-core)
  • Browser: Chrome, Firefox, Safari (default: Chrome)

Input Example

Make a React modal component accessible:
- Framework: React + TypeScript
- WCAG Level: AA
- Requirements:
  - Focus trap (focus stays inside the modal)
  - Close with ESC key
  - Close by clicking the background
  - Title/description read by screen readers

Instructions

Step 1: Use Semantic HTML

Use meaningful HTML elements to make the structure clear.

Tasks:

  • Use semantic tags: <button>, <nav>, <main>, <header>, <footer>, etc.
  • Avoid overusing <div> and <span>
  • Use heading hierarchy (<h1> ~ <h6>) correctly
  • Connect <label> with <input>

Example (❌ Bad vs ✅ Good):

<!-- ❌ Bad example: using only div and span -->
<div class="header">
  <span class="title">My App</span>
  <div class="nav">
    <div class="nav-item" onclick="navigate()">Home</div>
    <div class="nav-item" onclick="navigate()">About</div>
  </div>
</div>

<!-- ✅ Good example: semantic HTML -->
<header>
  <h1>My App</h1>
  <nav aria-label="Main navigation">
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/about">About</a></li>
    </ul>
  </nav>
</header>

Form Example:

<!-- ❌ Bad example: no label -->
<input type="text" placeholder="Enter your name">

<!-- ✅ Good example: label connected -->
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>

<!-- Or wrap with label -->
<label>
  Email:
  <input type="email" name="email" required>
</label>

Step 2: Implement Keyboard Navigation

Ensure all features are usable without a mouse.

Tasks:

  • Move focus with Tab and Shift+Tab
  • Activate buttons with Enter/Space
  • Navigate lists/menus with arrow keys
  • Close modals/dropdowns with ESC
  • Use tabindex appropriately

Decision Criteria:

  • Interactive elements → tabindex="0" (focusable)
  • Exclude from focus order → tabindex="-1" (programmatic focus only)
  • Do not change focus order → avoid using tabindex="1+"

Example (React Dropdown):

import React, { useState, useRef, useEffect } from 'react';

interface DropdownProps {
  label: string;
  options: { value: string; label: string }[];
  onChange: (value: string) => void;
}

function AccessibleDropdown({ label, options, onChange }: DropdownProps) {
  const [isOpen, setIsOpen] = useState(false);
  const [selectedIndex, setSelectedIndex] = useState(0);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const listRef = useRef<HTMLUListElement>(null);

  // Keyboard handler
  const handleKeyDown = (e: React.KeyboardEvent) => {
    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        if (!isOpen) {
          setIsOpen(true);
        } else {
          setSelectedIndex((prev) => (prev + 1) % options.length);
        }
        break;

      case 'ArrowUp':
        e.preventDefault();
        if (!isOpen) {
          setIsOpen(true);
        } else {
          setSelectedIndex((prev) => (prev - 1 + options.length) % options.length);
        }
        break;

      case 'Enter':
      case ' ':
        e.preventDefault();
        if (isOpen) {
          onChange(options[selectedIndex].value);
          setIsOpen(false);
          buttonRef.current?.focus();
        } else {
          setIsOpen(true);
        }
        break;

      case 'Escape':
        e.preventDefault();
        setIsOpen(false);
        buttonRef.current?.focus();
        break;
    }
  };

  return (
    <div className="dropdown">
      <button
        ref={buttonRef}
        onClick={() => setIsOpen(!isOpen)}
        onKeyDown={handleKeyDown}
        aria-haspopup="listbox"
        aria-expanded={isOpen}
        aria-labelledby="dropdown-label"
      >
        {label}
      </button>

      {isOpen && (
        <ul
          ref={listRef}
          role="listbox"
          aria-labelledby="dropdown-label"
          onKeyDown={handleKeyDown}
          tabIndex={-1}
        >
          {options.map((option, index) => (
            <li
              key={option.value}
              role="option"
              aria-selected={index === selectedIndex}
              onClick={() => {
                onChange(option.value);
                setIsOpen(false);
              }}
            >
              {option.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Step 3: Add ARIA Attributes

Provide additional context for screen readers.

Tasks:

  • aria-label: Define the element's name
  • aria-labelledby: Reference another element as a label
  • aria-describedby: Provide additional description
  • aria-live: Announce dynamic content changes
  • aria-hidden: Hide from screen readers

Checklist:

  • All interactive elements have clear labels
  • Button purpose is clear (e.g., "Submit form" not "Click")
  • State change announcements (aria-live)
  • Decorative images use alt="" or aria-hidden="true"

Example (Modal):

function AccessibleModal({ isOpen, onClose, title, children }) {
  const modalRef = useRef<HTMLDivElement>(null);

  // Focus trap when modal opens
  useEffect(() => {
    if (isOpen) {
      modalRef.current?.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
      aria-describedby="modal-description"
      ref={modalRef}
      tabIndex={-1}
      onKeyDown={(e) => {
        if (e.key === 'Escape') {
          onClose();
        }
      }}
    >
      <div className="modal-overlay" onClick={onClose} aria-hidden="true" />

      <div className="modal-content">
        <h2 id="modal-title">{title}</h2>
        <div id="modal-description">
          {children}
        </div>

        <button onClick={onClose} aria-label="Close modal">
          <span aria-hidden="true">×</span>
        </button>
      </div>
    </div>
  );
}

aria-live Example (Notifications):

function Notification({ message, type }: { message: string; type: 'success' | 'error' }) {
  return (
    <div
      role="alert"
      aria-live="assertive"  // Immediate announcement (error), "polite" announces in turn
      aria-atomic="true"     // Read the entire content
      className={`notification notification-${type}`}
    >
      {type === 'error' && <span aria-label="Error">⚠️</span>}
      {type === 'success' && <span aria-label="Success">✅</span>}
      {message}
    </div>
  );
}

Step 4: Color Contrast and Visual Accessibility

Ensure sufficient contrast ratios for users with visual impairments.

Tasks:

  • WCAG AA: text 4.5:1, large text 3:1
  • WCAG AAA: text 7:1, large text 4.5:1
  • Do not convey information by color alone (use icons, patterns alongside)
  • Clearly indicate focus (outline)

Example (CSS):

/* ✅ Sufficient contrast (text #000 on #FFF = 21:1) */
.button {
  background-color: #0066cc;
  color: #ffffff;  /* contrast ratio 7.7:1 */
}

/* ✅ Focus indicator */
button:focus,
a:focus {
  outline: 3px solid #0066cc;
  outline-offset: 2px;
}

/* ❌ outline: none is forbidden! */
button:focus {
  outline: none;  /* Never use this */
}

/* ✅ Indicate state with color + icon */
.error-message {
  color: #d32f2f;
  border-left: 4px solid #d32f2f;
}

.error-message::before {
  content: '⚠️';
  margin-right: 8px;
}

Step 5: Accessibility Testing

Validate accessibility with automated and manual testing.

Tasks:

  • Automated scan with axe DevTools
  • Check Lighthouse Accessibility score
  • Test all features with keyboard only
  • Screen reader testing (NVDA, VoiceOver)

Example (Jest + axe-core):

import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import AccessibleButton from './AccessibleButton';

expect.extend(toHaveNoViolations);

describe('AccessibleButton', () => {
  it('should have no accessibility violations', async () => {
    const { container } = render(
      <AccessibleButton onClick={() => {}}>
        Click Me
      </AccessibleButton>
    );

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

  it('should be keyboard accessible', () => {
    const handleClick = jest.fn();
    const { getByRole } = render(
      <AccessibleButton onClick={handleClick}>
        Click Me
      </AccessibleButton>
    );

    const button = getByRole('button');

    // Enter key
    button.focus();
    fireEvent.keyDown(button, { key: 'Enter' });
    expect(handleClick).toHaveBeenCalled();

    // Space key
    fireEvent.keyDown(button, { key: ' ' });
    expect(handleClick).toHaveBeenCalledTimes(2);
  });
});

Output format

Basic Checklist

## Accessibility Checklist

### Semantic HTML
- [x] Use semantic HTML tags (`<button>`, `<nav>`, `<main>`, etc.)
- [x] Heading hierarchy is correct (h1 → h2 → h3)
- [x] All form labels are connected

### Keyboard Navigation
- [x] All interactive elements accessible via Tab
- [x] Buttons activated with Enter/Space
- [x] Modals/dropdowns closed with ESC
- [x] Focus indicator is clear (outline)

### ARIA
- [x] `role` used appropriately
- [x] `aria-label` or `aria-labelledby` provided
- [x] `aria-live` used for dynamic content
- [x] Decorative elements use `aria-hidden="true"`

### Visual
- [x] Color contrast meets WCAG AA (4.5:1)
- [x] Information not conveyed by color alone
- [x] Text size can be adjusted
- [x] Responsive design

### Testing
- [x] 0 axe DevTools violations
- [x] Lighthouse Accessibility score 90+
- [x] Keyboard test passed
- [x] Screen reader test completed

Constraints

Mandatory Rules (MUST)

  1. Keyboard Accessibility: All features must be usable without a mouse

- Support Tab, Enter, Space, arrow keys, and ESC - Implement focus trap (for modals)

  1. Alternative Text: All images must have an alt attribute

- Meaningful images: descriptive alt text - Decorative images: alt="" (screen reader ignores)

  1. Clear Labels: All form inputs must have an associated label

- <label for="..."> or aria-label - Do not use placeholder alone as a substitute for a label

Prohibited Actions (MUST NOT)

  1. Do Not Remove Outline: Never use outline: none

- Disastrous for keyboard users - Must provide a custom focus style instead

  1. Do Not Use tabindex > 0: Avoid changing focus order

- Keep DOM order logical - Exception: only when there is a special reason

  1. Do Not Convey Information by Color Alone: Accompany with icons or text

- Consider users with color blindness - e.g., "Click red item" → "Click ⚠️ Error item"

Examples

Example 1: Accessible Form

function AccessibleContactForm() {
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [submitStatus, setSubmitStatus] = useState<'idle' | 'success' | 'error'>('idle');

  return (
    <form onSubmit={handleSubmit} noValidate>
      <h2 id="form-title">Contact Us</h2>
      <p id="form-description">Please fill out the form below to get in touch.</p>

      {/* Name */}
      <div className="form-group">
        <label htmlFor="name">
          Name <span aria-label="required">*</span>
        </label>
        <input
          type="text"
          id="name"
          name="name"
          required
          aria-required="true"
          aria-invalid={!!errors.name}
          aria-describedby={errors.name ? 'name-error' : undefined}
        />
        {errors.name && (
          <span id="name-error" role="alert" className="error">
            {errors.name}
          </span>
        )}
      </div>

      {/* Email */}
      <div className="form-group">
        <label htmlFor="email">
          Email <span aria-label="required">*</span>
        </label>
        <input
          type="email"
          id="email"
          name="email"
          required
          aria-required="true"
          aria-invalid={!!errors.email}
          aria-describedby={errors.email ? 'email-error' : 'email-hint'}
        />
        <span id="email-hint" className="hint">
          We'll never share your email.
        </span>
        {errors.email && (
          <span id="email-error" role="alert" className="error">
            {errors.email}
          </span>
        )}
      </div>

      {/* Submit button */}
      <button type="submit" disabled={submitStatus === 'loading'}>
        {submitStatus === 'loading' ? 'Submitting...' : 'Submit'}
      </button>

      {/* Success/failure messages */}
      {submitStatus === 'success' && (
        <div role="alert" aria-live="polite" className="success">
          ✅ Form submitted successfully!
        </div>
      )}

      {submitStatus === 'error' && (
        <div role="alert" aria-live="assertive" className="error">
          ⚠️ An error occurred. Please try again.
        </div>
      )}
    </form>
  );
}

Example 2: Accessible Tab UI

function AccessibleTabs({ tabs }: { tabs: { id: string; label: string; content: React.ReactNode }[] }) {
  const [activeTab, setActiveTab] = useState(0);

  const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
    switch (e.key) {
      case 'ArrowRight':
        e.preventDefault();
        setActiveTab((index + 1) % tabs.length);
        break;
      case 'ArrowLeft':
        e.preventDefault();
        setActiveTab((index - 1 + tabs.length) % tabs.length);
        break;
      case 'Home':
        e.preventDefault();
        setActiveTab(0);
        break;
      case 'End':
        e.preventDefault();
        setActiveTab(tabs.length - 1);
        break;
    }
  };

  return (
    <div>
      {/* Tab List */}
      <div role="tablist" aria-label="Content sections">
        {tabs.map((tab, index) => (
          <button
            key={tab.id}
            role="tab"
            id={`tab-${tab.id}`}
            aria-selected={activeTab === index}
            aria-controls={`panel-${tab.id}`}
            tabIndex={activeTab === index ? 0 : -1}
            onClick={() => setActiveTab(index)}
            onKeyDown={(e) => handleKeyDown(e, index)}
          >
            {tab.label}
          </button>
        ))}
      </div>

      {/* Tab Panels */}
      {tabs.map((tab, index) => (
        <div
          key={tab.id}
          role="tabpanel"
          id={`panel-${tab.id}`}
          aria-labelledby={`tab-${tab.id}`}
          hidden={activeTab !== index}
          tabIndex={0}
        >
          {tab.content}
        </div>
      ))}
    </div>
  );
}

Best practices

  1. Semantic HTML First: ARIA is a last resort

- Using the correct HTML element makes ARIA unnecessary - e.g., <button> vs <div role="button">

  1. Focus Management: Manage focus on page transitions in SPAs

- Move focus to main content on new page load - Provide skip links ("Skip to main content")

  1. Error Messages: Clear and helpful error messages

- "Invalid input" ❌ → "Email must be in format: example@domain.com" ✅

References

Metadata

Version

  • Current Version: 1.0.0
  • Last Updated: 2025-01-01
  • Compatible Platforms: Claude, ChatGPT, Gemini

Related Skills

Tags

#accessibility #a11y #WCAG #ARIA #screen-reader #keyboard-navigation #frontend

适合场景

01

用户想查找某类 Agent Skill 时

02

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

03

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

04

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

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

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

能力 4

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

能力 5

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

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

平台分布

Claude Code

29.63%
按下载量换算29,803

Codex

22.09%
按下载量换算22,219

Gemini CLI

17.87%
按下载量换算17,974

OpenCode

12.18%
按下载量换算12,251

Antigravity

7.69%
按下载量换算7,735

Cursor

3.85%
按下载量换算3,872

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

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

安装前确认

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

来源信息

继续浏览同类 Skills