Dsantiagomj

accessibility

Web accessibility (a11y) patterns and WCAG compliance. Covers WCAG 2.2 Level A/AA/AAA, ARIA attributes, keyboard navigation, screen reader support, color contrast, and semantic HTML. Trigger: When implementing accessibility features, when building accessible components, when ensuring WCAG compliance, when adding ARIA attributes, when handling keyboard navigation.

Dsantiagomj 0 Updated 7mo ago

Resources

1
GitHub

Install

npx skillscat add dsantiagomj/dsmj-ai-toolkit/accessibility

Install via the SkillsCat registry.

About this skill

We need to produce a 2-3 sentence plain-text summary, objective, factual, no marketing language, no superlatives, no calls to action. No bullet points, no headings, no markdown. At most 60 words. Provide only the summary text. We need to capture: skill does: provides web accessibility patterns and WCAG compliance guidance, covering WCAG 2.2 levels, ARIA, keyboard navigation, screen reader support, color contrast, semantic HTML. Problem it solves: ensures accessible web components, helps meet WCAG compliance, avoid inaccessible UI.

SKILL.md

Accessibility - WCAG Compliance & A11y Best Practices

Build inclusive web experiences for all users


When to Use This Skill

Use this skill when:

  • Implementing accessible components
  • Ensuring WCAG compliance
  • Adding ARIA attributes
  • Handling keyboard navigation
  • Supporting screen readers
  • Checking color contrast

Critical Patterns

Pattern 1: Semantic HTML

When: Building accessible structure

Good:

<!-- ✅ Semantic elements -->
<header>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>
<main>
  <article>
    <h1>Article Title</h1>
    <p>Content...</p>
  </article>
</main>

Bad:

<!-- ❌ Divs for everything -->
<div class="header">
  <div class="nav">
    <div onclick="navigate()">Home</div>
  </div>
</div>

Why: Semantic HTML provides structure for screen readers and enables automatic keyboard navigation.


Pattern 2: Keyboard Navigation

When: Making interactive elements accessible

Good:

<!-- ✅ Button is focusable -->
<button onClick={handleClick}>Click me</button>

<!-- ✅ Div with proper keyboard support -->
<div
  role="button"
  tabIndex={0}
  onClick={handleClick}
  onKeyDown={(e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      handleClick();
    }
  }}
>
  Click me
</div>

Bad:

<!-- ❌ Non-focusable div -->
<div onclick="handleClick()">Click me</div>

Why: All interactive elements must be keyboard accessible. Use native elements when possible.


Pattern 3: ARIA Labels

When: Providing accessible names

Good:

<!-- ✅ aria-label for icon buttons -->
<button aria-label="Close dialog">
  <svg><!-- X icon --></svg>
</button>

<!-- ✅ aria-labelledby for dialogs -->
<h2 id="dialog-title">Confirm Delete</h2>
<div role="dialog" aria-labelledby="dialog-title">
  <p>Are you sure?</p>
</div>

Bad:

<!-- ❌ No accessible name -->
<button>
  <svg><!-- X icon --></svg>
</button>

Why: Screen readers need text labels to understand interactive elements.


Pattern 4: Form Accessibility

When: Building accessible forms

Good:

<!-- ✅ Explicit label association -->
<label htmlFor="email">Email *</label>
<input
  id="email"
  type="email"
  aria-required="true"
  aria-invalid={hasError}
  aria-describedby={hasError ? "email-error" : undefined}
/>
{hasError && (
  <div id="email-error" role="alert">
    Please enter a valid email
  </div>
)}

Bad:

<!-- ❌ No label -->
<input type="email" placeholder="Email" />

Why: Labels are required for screen readers. Error messages must be announced.


Pattern 5: Color Contrast

When: Ensuring readability

Good:

/* ✅ Meets AA (7:1) */
.text {
  color: #595959;
  background: #ffffff;
}

Bad:

/* ❌ Fails AA (4.5:1) */
.text {
  color: #777777;
  background: #ffffff;
}

Why: WCAG AA requires 4.5:1 for normal text, 3:1 for large text.


Code Examples

Example 1: Accessible Form with Validation

export function SignupForm() {
  const [errors, setErrors] = useState<Record<string, string>>({});

  return (
    <form onSubmit={handleSubmit}>
      <div className="space-y-1">
        <label htmlFor="email" className="block text-sm font-medium">
          Email Address *
        </label>
        <input
          id="email"
          type="email"
          aria-required="true"
          aria-invalid={!!errors.email}
          aria-describedby={errors.email ? 'email-error' : undefined}
          className={cn(
            'border rounded-lg px-3 py-2',
            errors.email && 'border-red-500'
          )}
        />
        {errors.email && (
          <p id="email-error" role="alert" className="text-sm text-red-600">
            {errors.email}
          </p>
        )}
      </div>

      <button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded-lg">
        Sign Up
      </button>
    </form>
  );
}

Example 2: Keyboard-Accessible Custom Dropdown

export function Dropdown({ items, onSelect }: DropdownProps) {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button
        onClick={() => setIsOpen(!isOpen)}
        onKeyDown={(e) => {
          if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            setIsOpen(!isOpen);
          }
        }}
        aria-haspopup="listbox"
        aria-expanded={isOpen}
      >
        Select Option
      </button>

      {isOpen && (
        <ul role="listbox" tabIndex={-1}>
          {items.map((item, index) => (
            <li
              key={item.id}
              role="option"
              tabIndex={0}
              onClick={() => onSelect(item)}
              onKeyDown={(e) => {
                if (e.key === 'Enter') onSelect(item);
              }}
            >
              {item.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

For comprehensive examples and detailed implementations, see the references/ folder.


Quick Reference

WCAG AA Requirements

  • ✅ 4.5:1 text contrast (3:1 for large text)
  • ✅ 3:1 UI component contrast
  • ✅ All functionality keyboard accessible
  • ✅ Visible focus indicators
  • ✅ Text resizable to 200%
  • ✅ Form inputs labeled
  • ✅ Meaningful alt text
  • ✅ Logical heading structure

Component States

  • Default
  • Hover
  • Focus (keyboard)
  • Active
  • Disabled
  • Error

Progressive Disclosure

For detailed implementations:


References


Maintained by dsmj-ai-toolkit