Source profileQuality 89/100

eugenelim/agent-ready-repo/packs/frontend-engineering/.apm/skills/a11y-engineering/SKILL.md

a11y-engineering

Deep accessibility engineering beyond automated tooling — focus management architecture, ARIA role correctness under dynamic mutation, live-region discipline, keyboard contract specification, and manual WCAG 2.2 AA verification for the two criteria automated tools miss.

Source repository stars
15
Declared platforms
0
Static risk flags
0
Last source update
2026-08-05
Source checked
2026-08-05

Decision brief

What it does—and where it fits

Load this skill when an accessibility concern is the primary task — a dedicated accessibility audit, retrofitting broken patterns, or designing a new complex interaction where the a11y engineering is the central deliverable.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/eugenelim/agent-ready-repo --skill "packs/frontend-engineering/.apm/skills/a11y-engineering"
    Safe inspection promptEditorial

    Inspect the Agent Skill "a11y-engineering" from https://github.com/eugenelim/agent-ready-repo/blob/9563bc93aa5b0750b327be2fd95676ff2a5ec63b/packs/frontend-engineering/.apm/skills/a11y-engineering/SKILL.md at commit 9563bc93aa5b0750b327be2fd95676ff2a5ec63b. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Manual verification checklist

      For every interactive element on the surface:

      [ ] A visible focus indicator is present (no outline: none without a[ ] The focus indicator area is at least: perimeter of the component × 2px[ ] The focus indicator has a contrast ratio of at least 3:1 between the
    2. 02

      Output rendering

      Table — When presenting several items that share the same fields, render a Markdown table. Cap at 5 columns; beyond that, switch to a per-item detail list. Right-align numeric columns. Severity list — Lead each finding with a severity glyph — 🟥 blocker, 🟧 major, 🟨 minor, ⚪ ad…

      Table — When presenting several items that share the same fields, render a Markdown table. Cap at 5 columns; beyond that, switch to a per-item detail list. Right-align numeric columns. Severity list — Lead each finding…
    3. 03

      The two automated-tooling gaps

      Automated accessibility tools (pa11y, axe-core) cap at wcag21aa. Two WCAG 2.2 success criteria are new in 2.2 and require manual verification:

      Automated accessibility tools (pa11y, axe-core) cap at wcag21aa. Two WCAG 2.2 success criteria are new in 2.2 and require manual verification:2.4.11 Focus Appearance (AA): A keyboard focus indicator must have a minimum area of at least the perimeter of the unfocused component × 2 CSS pixels, and must have a contrast ratio of at least 3:1 between the focused a…2.5.8 Target Size Minimum (AA): Interactive targets must be at least 24×24 CSS pixels. If a target is smaller than 24×24, the spacing around it (to the nearest adjacent interactive element or page edge) must be at least…
    4. 04

      Focus management architecture

      Move focus programmatically — this is not optional — when:

      Move focus programmatically — this is not optional — when:The inert attribute disables all interaction and assistive technology access for an element and its descendants. Use it to suppress background content when a modal is open:inert is Baseline Widely Available as of 2023. Verify at web.dev/baseline before use in environments with older browser support requirements.
    5. 05

      When programmatic focus move is required

      Move focus programmatically — this is not optional — when:

      Move focus programmatically — this is not optional — when:

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score89/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars15SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    eugenelim/agent-ready-repo
    Skill path
    packs/frontend-engineering/.apm/skills/a11y-engineering/SKILL.md
    Commit
    9563bc93aa5b0750b327be2fd95676ff2a5ec63b
    License
    Apache-2.0
    Collected
    2026-08-05
    Default branch
    main
    View the original SKILL.md

    Skill: a11y-engineering

    Load this skill when an accessibility concern is the primary task — a dedicated accessibility audit, retrofitting broken patterns, or designing a new complex interaction where the a11y engineering is the central deliverable.

    Do not load this skill for routine component authoring; the accessibility section in frontend-engineering covers that case. Load a11y-engineering when:

    • Running a dedicated a11y audit on an existing surface
    • Retrofitting a surface that has known a11y failures
    • Designing a complex interaction pattern (combobox, data grid, drag-and-drop) where the keyboard and screen-reader contract is the primary concern
    • Investigating a focus management failure that pa11y/axe-core did not catch

    Output rendering

    Table — When presenting several items that share the same fields, render a Markdown table. Cap at ~5 columns; beyond that, switch to a per-item detail list. Right-align numeric columns. Severity list — Lead each finding with a severity glyph — 🟥 blocker, 🟧 major, 🟨 minor, ⚪ advisory — worst first, one finding per line, file:line anchor aligned. Status list — Lead each row with a status glyph — ● running, ✓ done, ○ idle, ⚠ blocked — status first, one item per line, labels aligned. Key–value / one record — For a single record's fields, use an aligned key: value list, not a two-row table.

    The two automated-tooling gaps

    Automated accessibility tools (pa11y, axe-core) cap at wcag21aa. Two WCAG 2.2 success criteria are new in 2.2 and require manual verification:

    2.4.11 Focus Appearance (AA): A keyboard focus indicator must have a minimum area of at least the perimeter of the unfocused component × 2 CSS pixels, and must have a contrast ratio of at least 3:1 between the focused and unfocused states. Automated tools cannot measure focus ring geometry or perform focused/unfocused contrast comparison — this check is always manual.

    2.5.8 Target Size Minimum (AA): Interactive targets must be at least 24×24 CSS pixels. If a target is smaller than 24×24, the spacing around it (to the nearest adjacent interactive element or page edge) must be at least 24px in all directions. Automated tools cannot reliably measure spacing between adjacent interactive elements — this check is always manual.

    Mark both explicitly in the evidence manifest under a11y result:

    a11y result:
      axe-core wcag21aa: [pass/fail + finding count]
      manual 2.4.11 Focus Appearance: [pass/fail + notes]
      manual 2.5.8 Target Size Minimum: [pass/fail + notes]
    

    Focus management architecture

    When programmatic focus move is required

    Move focus programmatically — this is not optional — when:

    TriggerRequired focus destination
    Modal opensFirst focusable element inside the dialog
    Modal closesThe element that invoked the dialog
    SPA route changePage <h1> (if it has tabindex="-1") or a skip-nav landmark
    Inline error appears (form submit)First invalid field, or the error summary element
    Async content inserted requiring immediate interactionThe inserted element or its first focusable child

    The DOM API sequence

    // 1. Make the element programmatically focusable if it is not natively focusable
    element.setAttribute('tabindex', '-1');
    
    // 2. Move focus
    element.focus({ preventScroll: false });
    
    // 3. For temporary targets: clean up tabindex after focus leaves
    element.addEventListener('blur', () => {
      element.removeAttribute('tabindex');
    }, { once: true });
    

    The inert attribute disables all interaction and assistive technology access for an element and its descendants. Use it to suppress background content when a modal is open:

    // On modal open:
    document.getElementById('main-content').setAttribute('inert', '');
    
    // On modal close:
    document.getElementById('main-content').removeAttribute('inert');
    

    inert is Baseline Widely Available as of 2023. Verify at web.dev/baseline before use in environments with older browser support requirements.


    ARIA role correctness under dynamic mutation

    These are the patterns AI-generated code gets wrong most often. Each entry shows the wrong pattern, why it fails, and the correct pattern.

    aria-expanded

    Wrong: aria-expanded="false" set once in the HTML template and never updated by JS.

    Why it fails: When the accordion/menu opens, the expanded state is not communicated to assistive technology. A screen reader user hears "collapsed" regardless of the actual state.

    Right:

    button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
    // Call this every time the toggle state changes, not just on initialization.
    

    aria-live — injecting region and content simultaneously

    Wrong:

    // Creating the live region and populating it at the same time
    const region = document.createElement('div');
    region.setAttribute('aria-live', 'polite');
    region.textContent = 'Results loaded: 12 items';
    document.body.appendChild(region);
    

    Why it fails: The screen reader has not registered the live region before the content is injected. The announcement is silently dropped in most screen readers.

    Right:

    <!-- Place an empty live region in the DOM on page load -->
    <div id="status" aria-live="polite" aria-atomic="true"></div>
    
    // Update its text content to trigger the announcement
    document.getElementById('status').textContent = 'Results loaded: 12 items';
    
    // Clear it after the announcement is no longer relevant
    setTimeout(() => {
      document.getElementById('status').textContent = '';
    }, 5000);
    

    aria-selected — not updated on keyboard navigation

    Wrong: aria-selected="true" set on the initially active tab; no JS updates when the user presses Left/Right to navigate.

    Why it fails: The screen reader announces the initially selected tab as selected regardless of which tab the user navigated to.

    Right: update aria-selected on every tab as keyboard navigation moves the selection:

    tabs.forEach(tab => {
      tab.setAttribute('aria-selected', tab === activeTab ? 'true' : 'false');
    });
    

    aria-sort — not updated on column sort

    Wrong: aria-sort="ascending" set on a column header and never updated.

    Why it fails: After the user re-sorts by a different column or reverses the sort, the announced sort direction is stale.

    Right: update aria-sort on the active column and remove it (or set it to "none") on all other columns every time the sort changes:

    headers.forEach(header => {
      header.setAttribute('aria-sort',
        header === sortedColumn
          ? (sortDirection === 'asc' ? 'ascending' : 'descending')
          : 'none'
      );
    });
    

    Live-region discipline

    Five rules that prevent silent announcement drops:

    1. Empty container in the DOM before content injection. The live region element must be present in the DOM before any text is written to it. Add it empty at page load; never create and populate it in the same frame.

    2. aria-live="polite" for informational updates. Toast success messages, search result counts, async load completions. The announcement waits for the user to finish their current interaction.

    3. aria-live="assertive" + aria-atomic="true" for errors. Validation summaries, session timeout warnings, destructive-action confirmations. The announcement interrupts the current interaction. Use sparingly — assertive announcements are disruptive.

    4. Never inject the live region element and content simultaneously. See the ARIA pattern above. This is the single most common live-region failure in generated code.

    5. Test with actual screen readers, not just axe-core. axe-core validates role structure but cannot exercise timing behavior. Test the announcement sequence with at minimum: VoiceOver + Safari on macOS/iOS and NVDA + Chrome on Windows.


    Keyboard contract specification

    Document a component's keyboard contract as a table before implementing it. This prevents the common failure mode of building the component, then discovering its keyboard behavior is undefined.

    Keyboard contract template:

    KeyActionFocus destinationScreen reader announcement
    TabEnter the componentFirst item"[item label], [role]"
    Down ArrowMove to next itemNext item"[item label]"
    Up ArrowMove to previous itemPrevious item"[item label]"
    Enter / SpaceActivate the focused itemUnchanged"[item label], selected"
    EscapeClose / dismissReturn to trigger"[component label], closed"
    HomeJump to first itemFirst item"[item label]"
    EndJump to last itemLast item"[item label]"

    Fill this template for every interactive component before writing JS.


    Complex pattern contracts

    Combobox (autocomplete/select)

    The combobox pattern requires three ARIA roles working together: the text input (role="combobox"), the popup list (role="listbox"), and the options (role="option"). The W3C Combobox pattern (APG) defines two variants: combobox with listbox popup and combobox with grid popup. Use the listbox variant for simple option selection; the grid variant for complex option cells (e.g., date pickers).

    Key contract points:

    • aria-expanded on the combobox element must reflect the open/closed state
    • aria-activedescendant on the combobox must point to the currently highlighted option's id
    • Keyboard: Down Arrow opens the popup and moves focus (logical), not physical focus — focus remains on the input; aria-activedescendant tracks selection

    Data grid

    A data grid (role="grid") requires row/cell navigation. Keyboard:

    • Tab enters the grid; navigates between interactive cells
    • Arrow keys navigate cells within the grid (two-dimensional navigation)
    • Enter/F2 activates a cell for editing
    • Escape exits edit mode; Tab/Shift+Tab navigate to next/previous interactive cell

    Each cell that contains interactive content uses role="gridcell"; column headers use role="columnheader" with aria-sort.

    Drag-and-drop (keyboard alternative required)

    Drag-and-drop without a keyboard alternative fails WCAG 2.1 Success Criterion 2.1.1 (Keyboard). A keyboard alternative is required. Two acceptable patterns:

    1. Cut/paste model: select an item (Space), navigate to the target position (Arrow keys), paste (Space or Enter).
    2. Directional reorder: select an item (Space), then use Alt+Arrow or Ctrl+Arrow to move it one position at a time.

    Announce each reorder with a live region: "Item moved from position 3 to position 1."


    Manual verification checklist

    WCAG 2.4.11 Focus Appearance

    For every interactive element on the surface:

    • A visible focus indicator is present (no outline: none without a visible replacement)
    • The focus indicator area is at least: perimeter of the component × 2px (a 2px solid outline around the component satisfies this for most controls)
    • The focus indicator has a contrast ratio of at least 3:1 between the focused and unfocused states

    Measurement: Open DevTools, tab to the element, measure the computed outline or box-shadow. Verify contrast with a contrast checker using the actual computed focus color against the background adjacent to the indicator.

    WCAG 2.5.8 Target Size Minimum

    For every interactive element on the surface:

    • Touch/click targets are at least 24×24 CSS pixels, OR
    • If smaller than 24×24, the offset (distance to the nearest adjacent interactive element) is at least 24px in all directions

    Measurement: Open DevTools, use the element inspector to read the computed width and height. For offset measurement, check the layout by measuring the gap between adjacent interactive elements.


    Remediation priority order

    When an audit finds multiple violations, prioritize in this order:

    1. New violations introduced by this change — blocking; must be fixed in this PR. A change that introduces an a11y regression ships blocked.
    2. Existing Blocker findings — WCAG AA violations on core user flows (form submission, navigation, primary actions). Fix as ride-alongs if in the same area; defer to a dedicated a11y sprint if in unrelated code.
    3. Existing Major findings — WCAG AA violations on secondary flows or non-blocking patterns. Record in known exceptions with rationale and planned resolution.
    4. Existing Minor findings — Polish and best-practice items. Record in known exceptions.

    A violation documented in known exceptions with a rationale and owner is not a failure — it is honest accounting. A violation that is silently omitted from the evidence manifest is a failure.

    Alternatives

    Compare before choosing

    Computed 10043,034

    coreyhaines31/marketingskills

    ab-testing

    When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program

    Computed 100165

    JasonColapietro/suede-creator-skills

    suede-ab-testing

    Suede-owned experimentation discipline for hypotheses, sample sizing, test duration, significance, and repeatable experiment programs. Use when comparing variants, deciding whether a result is reliable, or building an experiment backlog and cadence. NOT FOR: analytics instrumentation (use suede-analytics), post-click conversion diagnosis (use suede-site-alchemy), or writing the variant copy itself (use suede-copy).

    Computed 1007

    narrative-io/narrative-skills-marketplace

    design-analysis

    Translate a fuzzy analytical question into a rigorous investigation plan. Interrogates the ask, grounds the plan in the available data dictionary, applies analytical best practices, and produces a structured brief of query specifications for a downstream query-writing skill. Plans, does not write SQL. Use when: "why did X drop", "is there a relationship between A and B", "who are our highest-value customers", "what's driving the change in Y", "investigate this trend", "design an analysis for", "

    Computed 1007

    event4u-app/agent-config

    existing-ui-audit

    Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.