↗
Resource profile / Web Accessibility Review
About this skill

Workflow & requirements

Accessibility (a11y)

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

Evidence-led audit workflow

When a rendered page is available:

  1. Run a live Lighthouse Accessibility audit when that capability is available; with Chrome DevTools MCP, use lighthouse_audit. Use mobile navigation mode for a general public page or snapshot mode when reloading would lose authenticated or user-created state.
  2. Use failed audit nodes to localize the relevant component or template instead of searching the whole repository for generic patterns.
  3. Inspect a rendered accessibility-tree snapshot for names, roles, states, landmarks, and heading structure; with Chrome DevTools MCP, use take_snapshot. Exercise the affected flow with the keyboard.
  4. Fix the source, then re-run the same audit and manual interaction.

If the live tools are unavailable, use Lighthouse CLI or axe for automated coverage and complete the same manual checks. Automated tools detect only a subset of accessibility barriers: a score of 100 is not WCAG conformance, and a low score does not replace issue-level evidence.

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:

<!-- ❌ Missing alt -->
<img src="chart.png">

<!-- ✅ Descriptive alt -->
<img src="chart.png" alt="Bar chart showing 40% increase in Q3 sales">

<!-- ✅ Decorative image (empty alt) -->
<img src="decorative-border.png" alt="" role="presentation">

<!-- ✅ Complex image with longer description -->
<figure>
  <img src="infographic.png" alt="2024 market trends infographic" 
       aria-describedby="infographic-desc">
  <figcaption id="infographic-desc">
    <!-- Detailed description -->
  </figcaption>
</figure>

Icon buttons need accessible names:

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

<!-- ✅ Using aria-label -->
<button aria-label="Open menu">
  <svg aria-hidden="true"><!-- menu icon --></svg>
</button>

<!-- ✅ Using visually hidden text -->
<button>
  <svg aria-hidden="true"><!-- menu icon --></svg>
  <span class="visually-hidden">Open menu</span>
</button>

Visually hidden class:

.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
/* ❌ 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:

<!-- ❌ Only color indicates error -->
<input class="error-border">
<style>.error-border { border-color: red; }</style>

<!-- ✅ Color + icon + text -->
<div class="field-error">
  <input aria-invalid="true" aria-describedby="email-error">
  <span id="email-error" class="error-message">
    <svg aria-hidden="true"><!-- error icon --></svg>
    Please enter a valid email address
  </span>
</div>

Media alternatives (1.2)

<!-- Video with captions -->
<video controls>
  <source src="video.mp4" type="video/mp4">
  <track kind="captions" src="captions.vtt" srclang="en" label="English" default>
  <track kind="descriptions" src="descriptions.vtt" srclang="en" label="Descriptions">
</video>

<!-- Audio with transcript -->
<audio controls>
  <source src="podcast.mp3" type="audio/mp3">
</audio>
<details>
  <summary>Transcript</summary>
  <p>Full transcript text...</p>
</details>

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.

<!-- ❌ Non-interactive element with click only: not focusable, no keyboard activation -->
<div class="card" onclick="handleAction()">Open</div>

<!-- ✅ Best: use a native button -->
<button type="button" onclick="handleAction()">Open</button>
// ✅ 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
// <button> — 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)

/* ❌ 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.

/* ✅ 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;
}

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.

/* ✅ 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)

// 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)

/* 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)

<!-- ❌ No language specified -->
<html>

<!-- ✅ Language specified -->
<html lang="en">

<!-- ✅ Language changes within page -->
<p>The French word for hello is <span lang="fr">bonjour</span>.</p>

Consistent navigation (3.2.3)

<!-- Navigation should be consistent across pages -->
<nav aria-label="Main">
  <ul>
    <li><a href="/" aria-current="page">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>

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.

<!-- ✅ Auto-fill shipping address from billing -->
<fieldset>
  <legend>Shipping address</legend>
  <label>
    <input type="checkbox" id="same-as-billing" checked>
    Same as billing address
  </label>
  <!-- Fields auto-populated when checked -->
</fieldset>

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)

<!-- ✅ Allow paste in password fields -->
<input type="password" id="password" autocomplete="current-password">

<!-- ✅ Offer passwordless alternatives -->
<button type="button">Sign in with passkey</button>
<button type="button">Email me a login link</button>

Robust

ARIA usage (4.1.2)

Prefer native elements:

<!-- ❌ ARIA role on div -->
<div role="button" tabindex="0">Click me</div>

<!-- ✅ Native button -->
<button>Click me</button>

<!-- ❌ ARIA checkbox -->
<div role="checkbox" aria-checked="false">Option</div>

<!-- ✅ Native checkbox -->
<label><input type="checkbox"> Option</label>

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

Prefer a live Lighthouse audit that returns failing rendered nodes directly to the agent. With Chrome DevTools MCP, this is lighthouse_audit. Otherwise:

# 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.


Common issues by impact

Critical (fix immediately)

  1. Missing form labels
  2. Missing image alt text
  3. Insufficient color contrast
  4. Keyboard traps
  5. No focus indicators

Serious (fix before launch)

  1. Missing page language
  2. Missing heading structure
  3. Non-descriptive link text
  4. Auto-playing media
  5. Missing skip links

Moderate (fix soon)

  1. Missing ARIA labels on icons
  2. Inconsistent navigation
  3. Missing error identification
  4. Timing without controls
  5. Missing landmark regions

References

PACKAGE TRANSPARENCY

Inspect before installing

Source: Addy Osmani · MIT · SHA-256 shown alongside the download.

8 files21646 ZIP bytes0 script/code files

License file included. A license and checksum are not a security certification. Review package instructions and scripts before running them.

View files and uncompressed sizes

An adaptation record is bundled. Inspect the declared changes and archived original before use. Review adaptation and original-file hashes →

Machine-readable installation guide →
CATALOG REVIEW NOTES

Know what you need before installing

Source and packaging checks recorded on 2026-10-03. These notes are not safety certification or measured task performance.

Requirements

A runnable page and an authorized browser context. Use suitable automated checks and manual keyboard/assistive-technology checks. Chrome DevTools MCP or Lighthouse is optional tooling, not bundled.

Costs, access & practical limits

Independent MIT project maintained by Addy Osmani; not official Google guidance or certification. The package does not provide Chrome DevTools MCP, Lighthouse, credentials, user telemetry or a model. Tool, model and infrastructure access may have separate costs. Check current W3C and Google documentation; automated checks cover a subset of issues and cannot establish WCAG conformance, application security, search rankings or field Core Web Vitals. Full skill/agent runtime evaluation has not been performed. Packaging links are adapted; required cross-skill reference documents are bundled, while related SKILL.md links open pinned upstream source and do not install other skills.

View the recorded checks
  • Pinned originals verified against Git blob hashes
  • Complete upstream MIT notice preserved
  • Core identity and local Markdown dependencies checked
  • Link adaptations declared and exact changed originals retained
  • Independent attribution and measurement limits disclosed

Upstream commit: afa8da942115f2961fdbfa80807ea0b232ff6c00

Runtime status: not tested by this catalog. Configure your client and test the skill in your own environment.

SCENARIOS

Inputs, criteria and recorded outcomes

Records are supplied by the site administrator and bound to a specific package. They are not third-party safety certification. This page does not execute skills.

BB Skills keyboard navigation: bounded Chromium checks

Reported passed · vafa8da942115.bb1

View input and acceptance criteria

Input

Inspect BB Skills skip-link behavior on home, library, installation guide and login at 390 and 1440 CSS pixels, plus mobile navigation. Use keyboard Tab/Enter; submit no forms.

Acceptance criteria

First Tab exposes the skip link; Enter focuses MAIN; next Tab enters content. Mobile menu opens and its Library link is keyboard reachable. This is a bounded site observation, not a full execution of the downloaded skill, screen-reader testing or WCAG certification.

Recorded outcome

Chromium keyboard-only skip-link flows on four public routes at two viewport widths and one mobile-menu flow; no screen reader, real mobile device, Lighthouse, WCAG certification or full skill execution.
{
  "format": 1,
  "status": "passed",
  "executed_at": "2026-10-03T04:04:29.488Z",
  "scope": "Chromium keyboard-only skip-link flows on four public routes at two viewport widths and one mobile-menu flow; no screen reader, real mobile device, Lighthouse, WCAG certification or full skill execution.",
  "flows": [
    {
      "path": "/",
      "width": 390,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    },
    {
      "path": "/library",
      "width": 390,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    },
    {
      "path": "/start",
      "width": 390,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    },
    {
      "path": "/login",
      "width": 390,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    },
    {
      "width": 390,
      "mobile_menu": {
        "open": true,
        "text": "Library",
        "inside": true
      }
    },
    {
      "path": "/",
      "width": 1440,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    },
    {
      "path": "/library",
      "width": 1440,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    },
    {
      "path": "/start",
      "width": 1440,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    },
    {
      "path": "/login",
      "width": 1440,
      "first": {
        "text": "Skip to content",
        "visible": true
      },
      "target": {
        "tag": "MAIN",
        "id": "main-content",
        "hash": "#main-content",
        "scrollBehavior": "auto"
      },
      "next_inside_main": true
    }
  ]
}
Evidence SHA-256: 6da2f2f1d3ee7e46997492c96b84b395cbb97fc584a84e04e5692396871095a3

Environment

Chromium Playwright browser; English locale; reduced-motion preference; 390x900 and 1440x900 viewports. Synthetic viewport, not a real mobile device.

Package SHA-256: a1ef9d1038ed7c117356762ca59a64a1050ca58612129870397e0a6a6ae3707b

Outcome recorded: 2026-10-03 04:04 UTC

Community reviews

★ New

Be the first to share your experience.

Sign in to leave a review →

More to explore

View all ↗