| name | fix-accessibility |
| description | Audit and fix WCAG 2.1 Level AA accessibility issues in BEEQ StencilJS components. Use for: adding missing ARIA attributes, fixing keyboard navigation, managing focus, fixing color contrast issues, adding screen reader support, fixing role usage, and writing accessibility-related E2E tests for bq-* components. |
| argument-hint | Component name or path (e.g. "bq-button" or packages/beeq/src/components/button/bq-button.tsx) |
Fix Accessibility in a BEEQ Component
When to Use
- A component fails an accessibility audit or axe scan
- A PR review flagged missing ARIA attributes
- Keyboard navigation is broken or incomplete
- Screen reader announces incorrect role, state, or label
- Focus is lost after a user interaction
Before You Start
- Read the accessibility instructions
- Read the component's
.tsx to understand its current HTML structure and shadow DOM
- Note which semantic HTML elements are used and whether shadow DOM parts are exposed
Audit Checklist
Work through each category in order:
1. Semantic HTML
- Interactive elements use native elements:
<button>, <a href>, <input>, <select>, <textarea>
- Use
<button> for actions, <a> for navigation
- Heading levels are not skipped inside complex components
-
<label> elements are associated to inputs via htmlFor / slotting, or aria-label/aria-labelledby
- Use
<ul>/<li> for lists, <nav> for navigation regions, <main>, <header>, <footer> for landmarks
- Do not add
role="button" to a <div> when a <button> can be used instead
2. Labels & Descriptions
3. Roles
4. State Attributes
Reflect interactive state with ARIA:
| State | Attribute |
|---|
| Open/closed (accordion, drawer, dropdown) | aria-expanded="true/false" |
| Selected (tab, option) | aria-selected="true/false" |
| Checked (checkbox, radio, toggle) | aria-checked="true/false" |
| Disabled | aria-disabled="true" (in addition to disabled where needed) |
| Required | aria-required="true" |
| Invalid | aria-invalid="true" + aria-describedby pointing to error message |
| Busy/loading | aria-busy="true" |
| Hidden decorative elements | aria-hidden="true" |
5. Keyboard Navigation
| Component type | Expected keyboard behavior |
|---|
| Button | Enter and Space activate |
| Link | Enter activates |
| Checkbox / Toggle | Space toggles |
| Accordion | Enter/Space expand; Tab moves between items |
| Select / Listbox | Enter/Space open; Arrow keys navigate options; Escape closes |
| Dialog / Drawer | Escape closes; focus trapped inside while open |
| Tabs | Arrow keys move between tabs; Tab moves to tab panel |
| Date picker | Arrow keys navigate calendar; Escape closes |
Implement keyboard handling with @Listen('keydown') or inline onKeyDown. Use event.key values, not event.keyCode.
6. Focus Management
7. Live Regions
8. Color & Contrast
9. Screen reader support
Implementation Patterns
Icon-only button
<button
aria-label={this.label}
aria-disabled={this.disabled ? 'true' : undefined}
>
<slot />
</button>
@Prop({ reflect: true }) label?: string;
Expandable element
<div
role="button"
aria-expanded={this.open ? 'true' : 'false'}
aria-controls="panel-id"
tabindex="0"
onKeyDown={this.handleKeyDown}
>
Error-linked input
<input
aria-describedby={this.hasError ? `${this.name}-error` : undefined}
aria-invalid={this.hasError ? 'true' : undefined}
/>
{this.hasError && (
<span id={`${this.name}-error`} role="alert">{this.errorMessage}</span>
)}
Writing Accessibility Tests
After fixing, add E2E assertions (see write-e2e-tests skill):
it('should have aria-label on icon-only button', async () => {
const { root } = await render(<bq-button only-icon label="Close" />);
const btn = (root as HTMLBqButtonElement).shadowRoot?.querySelector<HTMLButtonElement>('[part="button"]');
expect(btn).toEqualAttribute('aria-label', 'Close');
});
it('should have aria-expanded when open', async () => {
const { root, setProps } = await render(<bq-accordion />);
await setProps({ open: true });
const base = (root as HTMLBqAccordionElement).shadowRoot?.querySelector('[part="header"]');
expect(base).toEqualAttribute('aria-expanded', 'true');
});
Report format
Issues
List each problem with:
- Element / prop / line — location of the issue (markdown link, workspace-relative path).
- WCAG criterion — which criterion is violated (e.g. 1.3.1, 4.1.2).
- Severity —
critical (blocks assistive technology) | serious (significant barrier) | moderate (inconvenience) | minor.
- Fix — the concrete change needed.
Recommendations
Additional improvements that are not violations but would improve the experience for users of assistive technology.