Labsco
addyosmani logo

accessibility

β˜… 2,500

by addyosmani Β· part of addyosmani/web-quality-skills

Audit and improve web accessibility following WCAG 2.2 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible".

πŸ”₯πŸ”₯πŸ”₯πŸ”₯βœ“ VerifiedFreeNeeds API keys
🧩 One of 5 skills in the addyosmani/web-quality-skills package β€” works on its own, and pairs well with its siblings.

Audit and improve web accessibility following WCAG 2.2 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible".

Inspect the full instructions your agent will receiveExpand

This is the exact playbook injected into your agent when the skill activates β€” shown here so you can audit it before installing. You don't need to read it to use the skill.

by addyosmani

Audit and improve web accessibility following WCAG 2.2 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible". npx skills add https://github.com/addyosmani/web-quality-skills --skill accessibility Download ZIPGitHub2.5k

Accessibility (a11y)

Comprehensive accessibility guidelines based on WCAG 2.2 and Lighthouse accessibility audits. Goal: make content usable by everyone, including people with disabilities.

WCAG Principles: POUR

Principle Description Perceivable Content can be perceived through different senses Operable Interface can be operated by all users Understandable Content and interface are understandable Robust Content works with assistive technologies

Conformance levels

Level Requirement Target A Minimum accessibility Must pass AA Standard compliance Should pass (legal requirement in many jurisdictions) AAA Enhanced accessibility Nice to have

Perceivable

Text alternatives (1.1)

Images require alt text:

Copy & paste β€” that's it
 
 

 
 

 
 

 
 
 
 
 
 
 

Icon buttons need accessible names:

Copy & paste β€” that's it
 
 

 
 
 
 

 
 
 
 Open menu 
 

Visually hidden class:

Copy & paste β€” that's it
.visually-hidden {
 position: absolute;
 width: 1px;
 height: 1px;
 padding: 0;
 margin: -1px;
 overflow: hidden;
 clip: rect(0, 0, 0, 0);
 white-space: nowrap;
 border: 0;
}

Color contrast (1.4.3, 1.4.6)

Text Size AA minimum AAA enhanced Normal text (< 18px / < 14px bold) 4.5:1 7:1 Large text (β‰₯ 18px / β‰₯ 14px bold) 3:1 4.5:1 UI components & graphics 3:1 3:1

Copy & paste β€” that's it
/* ❌ Low contrast (2.5:1) */
.low-contrast {
 color: #999;
 background: #fff;
}

/* βœ… Sufficient contrast (7:1) */
.high-contrast {
 color: #333;
 background: #fff;
}

/* βœ… Focus states need contrast too (3:1 against background, WCAG 1.4.11) */
:focus-visible {
 outline: 2px solid currentColor;
 outline-offset: 2px;
}

Don't rely on color alone:

Copy & paste β€” that's it
 
 
 .error-border { border-color: red; } 

 
 
 
 
 
 Please enter a valid email address
 

Media alternatives (1.2)

Copy & paste β€” that's it
 
 
 
 
 
 

 
 
 
 
 
 Transcript 
 Full transcript text...

 

Operable

Keyboard accessible (2.1)

All functionality must be keyboard accessible. Prefer native interactive elements β€” <button>, <a href>, and form controls handle Enter/Space activation, focus, and assistive-tech semantics for free. Only add manual keyboard handling when you cannot use a native element.

Copy & paste β€” that's it
 
 Open

 
 Open 
Copy & paste β€” that's it
// βœ… When you MUST use a non-interactive element (e.g. div with role="button"),
// make it focusable AND handle keyboard activation. Do NOT add this to a native
// β€” Enter/Space already fire click, so you'd double-trigger.
element.setAttribute('role', 'button');
element.setAttribute('tabindex', '0');
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
 if (e.key === 'Enter' || e.key === ' ') {
 e.preventDefault();
 handleAction();
 }
});

No keyboard traps. Users must be able to Tab into and out of every component. Use the modal focus trap pattern for dialogsβ€”the native <dialog> element handles this automatically.

Focus visible (2.4.7)

Copy & paste β€” that's it
/* ❌ Never remove focus outlines */
*:focus { outline: none; }

/* βœ… Use :focus-visible for keyboard-only focus */
:focus {
 outline: none;
}

:focus-visible {
 outline: 2px solid currentColor; /* inherits text color β†’ already contrast-checked */
 outline-offset: 2px;
}

/* βœ… Or pick a brand color and verify β‰₯3:1 contrast against every background it lands on */
button:focus-visible {
 box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);
}

Focus not obscured (2.4.11) β€” new in 2.2

When an element receives keyboard focus, it must not be entirely hidden by other author-created content such as sticky headers, footers, or overlapping panels. At Level AAA (2.4.12), no part of the focused element may be hidden.

Copy & paste β€” that's it
/* βœ… Account for sticky headers when scrolling to focused elements */
:target {
 scroll-margin-top: 80px;
}

/* βœ… Ensure focused items clear fixed/sticky bars */
:focus {
 scroll-margin-top: 80px;
 scroll-margin-bottom: 60px;
}

Skip links (2.4.1)

Provide a skip link so keyboard users can bypass repetitive navigation. See the skip link pattern for full markup and styles.

Target size (2.5.8) β€” new in 2.2

Interactive targets must be at least 24 Γ— 24 CSS pixels (AA). Exceptions: inline text links, elements where the browser controls the size, and targets where a 24px circle centered on the bounding box does not overlap another target.

Copy & paste β€” that's it
/* βœ… Minimum target size */
button,
[role="button"],
input[type="checkbox"] + label,
input[type="radio"] + label {
 min-width: 24px;
 min-height: 24px;
}

/* βœ… Comfortable target size (recommended 44Γ—44) */
.touch-target {
 min-width: 44px;
 min-height: 44px;
 display: inline-flex;
 align-items: center;
 justify-content: center;
}

Dragging movements (2.5.7) β€” new in 2.2

Any action that requires dragging must have a single-pointer alternative (e.g., buttons, inputs). See the dragging movements pattern for a sortable-list example.

Timing (2.2)

Copy & paste β€” that's it
// Allow users to extend time limits
function showSessionWarning() {
 const modal = createModal({
 title: 'Session Expiring',
 content: 'Your session will expire in 2 minutes.',
 actions: [
 { label: 'Extend session', action: extendSession },
 { label: 'Log out', action: logout }
 ],
 timeout: 120000
 });
}

Motion (2.3)

Copy & paste β€” that's it
/* Respect reduced motion 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;
 }
}

Understandable

Page language (3.1.1)

Copy & paste β€” that's it
 
 

 
 

 
 The French word for hello is bonjour .

Consistent navigation (3.2.3)

Copy & paste β€” that's it
 
 
 
 
- Home 
 
- Products 
 
- About 
 

 

Consistent help (3.2.6) β€” new in 2.2

If a help mechanism (contact info, chat widget, FAQ link, self-help option) is repeated across multiple pages, it must appear in the same relative order each time. Users who rely on consistent placement shouldn't have to hunt for help on every page.

Form labels (3.3.2)

Every input needs a programmatically associated label. See the form labels pattern for explicit, implicit, and instructional examples.

Error handling (3.3.1, 3.3.3)

Announce errors to screen readers with role="alert" or aria-live, set aria-invalid="true" on invalid fields, and focus the first error on submit. See the error handling pattern for full markup and JS.

Redundant entry (3.3.7) β€” new in 2.2

Don't force users to re-enter information they already provided in the same session. Auto-populate from earlier steps, or let users select from previously entered values. Exceptions: security re-confirmation and content that has expired.

Copy & paste β€” that's it
 
 
 Shipping address 
 
 
 Same as billing address
 
 
 

Accessible authentication (3.3.8) β€” new in 2.2

Login flows must not rely on cognitive function tests (e.g., remembering a password, solving a puzzle) unless at least one of:

  • A copy-paste or autofill mechanism is available

  • An alternative method exists (e.g., passkey, SSO, email link)

  • The test uses object recognition or personal content (AA only; AAA removes this exception)

Copy & paste β€” that's it
 
 

 
 Sign in with passkey 
 Email me a login link 

Robust

ARIA usage (4.1.2)

Prefer native elements:

Copy & paste β€” that's it
 
 Click me

 
 Click me 

 
 Option

 
 Option 

When ARIA is needed, use the correct roles and states. See the ARIA tabs pattern for a complete tablist example.

Live regions (4.1.3)

Use aria-live regions to announce dynamic content changes without moving focus. See the live regions pattern for markup and a showNotification() helper.

Testing checklist

Automated testing

Copy & paste β€” that's it
# Lighthouse accessibility audit
npx lighthouse https://example.com --only-categories=accessibility

# axe-core
npm install @axe-core/cli -g
axe https://example.com

Manual testing

  • Keyboard navigation: Tab through entire page, use Enter/Space to activate

  • Screen reader: Test with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android)

  • Zoom: Content usable at 200% zoom

  • High contrast: Test with Windows High Contrast Mode

  • Reduced motion: Test with prefers-reduced-motion: reduce

  • Focus order: Logical and follows visual order

  • Target size: Interactive elements meet 24Γ—24px minimum

See the screen reader commands reference for VoiceOver and NVDA shortcuts.

References