Skip to content

09 — Fix Accessibility Issues

Fix accessibility issues found in a module or component via axe-core, Lighthouse, or manual audit.
Examples: Missing ARIA attributes, keyboard navigation broken, focus not visible, color contrast failing.


Read these files before executing the prompt:

docs/PRD/14_Accessibility_and_Compliance.md
docs/dev-frontend-guides/03_MODULE_DEVELOPMENT_LIFECYCLE.md # a11y checklist section
packages/modules/src/{MODULE_PATH}/ # all files in the affected module
packages/ui/src/ # if the issue is in a shared UI component

Read the following context files first:
- docs/PRD/14_Accessibility_and_Compliance.md
- docs/dev-frontend-guides/03_MODULE_DEVELOPMENT_LIFECYCLE.md
- {COMPONENT_FILES — list each file in the module directory}
Now fix the following accessibility issue:
**Component Path:** packages/{PACKAGE}/src/{COMPONENT_PATH}/
**Component Name:** {COMPONENT_NAME}
**Module ID:** {MODULE_ID}
**Issue Source:** {axe-core | Lighthouse | manual audit | screen reader testing}
**WCAG Criterion Violated:** {WCAG_CRITERION — e.g., 2.1.1 Keyboard, 4.1.2 Name/Role/Value, 1.4.3 Contrast}
**WCAG Level:** {A | AA}
**Issue Description:**
{DESCRIBE_THE_SPECIFIC_ACCESSIBILITY_PROBLEM}
**Current Behavior:**
{WHAT_HAPPENS_NOW — e.g., "Pressing Enter on accordion header does nothing", "Icon button has no accessible name"}
**Expected Behavior:**
{WHAT_SHOULD_HAPPEN — e.g., "Pressing Enter or Space toggles the accordion panel", "Icon button announces 'Close menu' to screen readers"}
**Steps to Reproduce:**
{HOW_TO_TRIGGER_THE_ISSUE — e.g., "Tab to the accordion header, press Enter — nothing happens"}
**Additional Context:**
{ANY_EXTRA_INFO — e.g., "This also affects M14 FAQ which uses the same accordion pattern"}
Fix the issue following these requirements:
- WCAG 2.1 AA compliance is mandatory for all components
- axe-core must report zero violations after the fix
- Keyboard navigation must work fully (Tab, Shift+Tab, Enter, Space, Escape, Arrow keys where applicable)
- Screen readers (VoiceOver, NVDA) must announce the correct state and role
- Focus must be visible at all times with a clear focus indicator
- Color contrast must meet 4.5:1 for normal text, 3:1 for large text
- ARIA attributes must follow WAI-ARIA 1.2 authoring practices
- No information may be conveyed only through color — use text, icons, or patterns as well
- Update the component's test file to include a11y regression tests
- Update Storybook stories if the fix changes the component's rendered output

  • The specific WCAG criterion violation is resolved
  • axe-core reports zero violations on the component (test with @axe-core/react or Storybook a11y addon)
  • Full keyboard navigation works: Tab, Shift+Tab, Enter, Space, Escape, and Arrow keys where applicable
  • Screen reader announces correct role, name, and state (test with VoiceOver on macOS)
  • Focus indicator is visible on all interactive elements (meets WCAG 2.4.7)
  • Color contrast meets minimum ratios: 4.5:1 for normal text, 3:1 for large text (WCAG 1.4.3)
  • ARIA attributes are correct and follow WAI-ARIA 1.2 authoring practices
  • No content is conveyed only through color (WCAG 1.4.1)
  • Component test file updated with a11y regression test covering the fixed issue
  • Storybook stories updated if the rendered output changed
  • Fix works correctly in all 8 hotel themes (different color palettes may affect contrast)
  • Fix does not break any existing functionality or visual design
  • pnpm test passes with the updated test file
  • No new TypeScript errors introduced

  1. Missing aria-label on icon-only buttons — Buttons with only an icon (close, menu, arrow) must have aria-label describing the action: <button aria-label="Close menu">.
  2. Missing alt text on images — Every &lt;img&gt; needs alt. Decorative images use alt="" with role="presentation". Content images need descriptive alt text.
  3. Focus indicator not visible — The default browser outline is often overridden by outline: none. Always provide a visible custom focus style using CSS custom properties for theme compatibility.
  4. Skip-to-content link missing — The page layout must include a skip link as the first focusable element that jumps to #main-content.
  5. aria-expanded not toggled — Accordion headers, dropdown triggers, and menu buttons must toggle aria-expanded="true" / aria-expanded="false" on state change.
  6. Focus not trapped in modals — When a modal or dialog is open, focus must be trapped inside it. Tab from the last element should cycle to the first. Escape must close the modal.
  7. Missing landmark roles — Page sections should use semantic HTML5 landmarks (&lt;nav&gt;, &lt;main&gt;, &lt;aside&gt;, &lt;footer&gt;) or explicit role attributes.
  8. Interactive elements not reachable by keyboard — Click handlers on &lt;div&gt; or &lt;span&gt; are not keyboard accessible. Use &lt;button&gt; or &lt;a&gt; or add role, tabindex="0", and key event handlers.
  9. Heading level hierarchy skipped — Heading levels must not skip (e.g., &lt;h2&gt; followed by &lt;h4&gt;). Maintain sequential order within each section.
  10. Form inputs without labels — Every &lt;input&gt;, &lt;select&gt;, and &lt;textarea&gt; must have an associated &lt;label&gt; via htmlFor, or use aria-label / aria-labelledby.

Filled-in prompt for M13 Accordion — Keyboard and ARIA issues:

Read the following context files first:
- docs/PRD/14_Accessibility_and_Compliance.md
- docs/dev-frontend-guides/03_MODULE_DEVELOPMENT_LIFECYCLE.md
- packages/modules/src/m13-accordion/index.tsx
- packages/modules/src/m13-accordion/m13-accordion.module.scss
- packages/modules/src/m13-accordion/m13-accordion.types.ts
- packages/modules/src/m13-accordion/m13-accordion.test.tsx
- packages/modules/src/m13-accordion/m13-accordion.stories.tsx
Now fix the following accessibility issue:
**Component Path:** packages/modules/src/m13-accordion/
**Component Name:** Accordion
**Module ID:** M13
**Issue Source:** axe-core + manual keyboard audit
**WCAG Criterion Violated:** 2.1.1 Keyboard (Level A), 4.1.2 Name/Role/Value (Level A)
**WCAG Level:** A
**Issue Description:**
The accordion component has three accessibility violations:
1. Pressing Enter or Space on an accordion header does not toggle the panel open/closed
2. The `aria-expanded` attribute is missing from accordion header buttons
3. The focus indicator is not visible on accordion panels when navigating with Tab
**Current Behavior:**
- Accordion headers respond to mouse click only — keyboard Enter and Space do nothing
- No `aria-expanded` attribute exists, so screen readers cannot announce whether a panel is open or closed
- When tabbing through accordion items, the focus ring is invisible due to `outline: none` in the SCSS with no replacement style
**Expected Behavior:**
- Pressing Enter or Space on a focused accordion header toggles the associated panel open/closed
- Each header has `aria-expanded="true"` when open and `aria-expanded="false"` when closed
- Each header has `aria-controls` pointing to the panel's `id`
- Focus ring is clearly visible on all accordion headers using the theme's focus token `var(--focus-ring-color)`
- Arrow Up/Down moves focus between accordion headers
**Steps to Reproduce:**
1. Open Storybook, navigate to M13 Accordion > Default story
2. Press Tab to focus the first accordion header
3. Press Enter — nothing happens (panel does not toggle)
4. Inspect the header element — no `aria-expanded` attribute present
5. Observe no visible focus indicator on the header
**Additional Context:**
This same accordion pattern is used by M14 FAQ. The fix here should also apply there.
Fix the issue following these requirements:
- WCAG 2.1 AA compliance is mandatory for all components
- axe-core must report zero violations after the fix
- Keyboard navigation must work fully (Tab, Shift+Tab, Enter, Space, Escape, Arrow keys where applicable)
- Screen readers (VoiceOver, NVDA) must announce the correct state and role
- Focus must be visible at all times with a clear focus indicator
- Color contrast must meet 4.5:1 for normal text, 3:1 for large text
- ARIA attributes must follow WAI-ARIA 1.2 authoring practices
- No information may be conveyed only through color — use text, icons, or patterns as well
- Update the component's test file to include a11y regression tests
- Update Storybook stories if the fix changes the component's rendered output