diff --git a/.claude/skills/coding-standards/SKILL.md b/.claude/skills/coding-standards/SKILL.md
index d34adf0f723b5..f2450a4cc2fcf 100644
--- a/.claude/skills/coding-standards/SKILL.md
+++ b/.claude/skills/coding-standards/SKILL.md
@@ -15,6 +15,7 @@ Coding standards for the Expensify App. Each standard is a standalone file in `r
| Performance | `PERF-*` | Render optimization, memo patterns, useEffect hygiene, data selection |
| Consistency | `CONSISTENCY-*` | Platform checks, magic values, unused props, ESLint discipline |
| Clean React Patterns | `CLEAN-REACT-PATTERNS-*` | Composition, component ownership, state structure |
+| Accessibility | `A11Y-*` | WCAG 2.2 AA compliance, screen reader support, inclusive interaction patterns |
## Quick Reference
@@ -50,6 +51,23 @@ Coding standards for the Expensify App. Each standard is a standalone file in `r
- [CLEAN-REACT-PATTERNS-4](rules/clean-react-4-no-side-effect-spaghetti.md) — No side-effect spaghetti
- [CLEAN-REACT-PATTERNS-5](rules/clean-react-5-narrow-state.md) — Keep state narrow
+### Accessibility (WCAG 2.2 AA)
+
+**Use React Native accessibility props.** React Native Web translates them to ARIA attributes automatically. Only use `aria-*` when a React Native equivalent isn't available. Reference: [React Native Accessibility](https://reactnative.dev/docs/accessibility)
+
+- [A11Y-1](rules/a11y-1-label-interactive-elements.md) — Label interactive elements
+- [A11Y-2](rules/a11y-2-semantic-roles.md) — Semantic accessibilityRole
+- [A11Y-3](rules/a11y-3-communicate-state.md) — Communicate component state
+- [A11Y-4](rules/a11y-4-touch-target-size.md) — Minimum 44x44 touch targets
+- [A11Y-5](rules/a11y-5-announce-dynamic-content.md) — Announce dynamic content
+- [A11Y-6](rules/a11y-6-accessible-images.md) — Accessible images
+- [A11Y-7](rules/a11y-7-no-color-only-info.md) — No color-only information
+- [A11Y-8](rules/a11y-8-modal-focus-management.md) — Modal focus management
+- [A11Y-9](rules/a11y-9-drag-alternatives.md) — Drag interaction alternatives
+- [A11Y-10](rules/a11y-10-respect-text-scaling.md) — Respect text scaling
+- [A11Y-11](rules/a11y-11-form-accessibility.md) — Form accessibility
+- [A11Y-12](rules/a11y-12-logical-focus-order.md) — Logical focus order
+
## Usage
**During development**: When writing or modifying `src/` files, consult the relevant standard files for detailed conditions, examples, and exceptions.
diff --git a/.claude/skills/coding-standards/rules/a11y-1-label-interactive-elements.md b/.claude/skills/coding-standards/rules/a11y-1-label-interactive-elements.md
new file mode 100644
index 0000000000000..12ad37c03aa0e
--- /dev/null
+++ b/.claude/skills/coding-standards/rules/a11y-1-label-interactive-elements.md
@@ -0,0 +1,65 @@
+---
+ruleId: A11Y-1
+title: Interactive elements must have accessible labels
+---
+
+## [A11Y-1] Interactive elements must have accessible labels
+
+### Reasoning
+
+Screen readers (VoiceOver/TalkBack) cannot convey the purpose of an interactive element without a text label. Icon-only buttons, image-only touchables, and components whose visible text is insufficient for context must provide `accessibilityLabel`. Without it, assistive technology announces the element as an unnamed control, making the app unusable for screen reader users. (WCAG 1.1.1, 4.1.2)
+
+### Incorrect
+
+```tsx
+// Icon-only button with no label — screen reader says "button"
+
+
+
+
+// Image-only touchable with no description
+
+
+
+```
+
+### Correct
+
+```tsx
+
+
+
+
+
+
+
+```
+
+---
+
+### Review Metadata
+
+Flag ONLY when ALL of these are true:
+
+- Element is interactive (`Pressable`, `TouchableOpacity`, `TouchableWithoutFeedback`, `PressableWithFeedback`, `Button`, or has `onPress`/`onLongPress`)
+- Element contains **no visible `` child** (icon-only, image-only, or SVG-only)
+- Element has **no** `accessibilityLabel` prop
+
+**DO NOT flag if:**
+
+- Element has a `` child that clearly describes the action
+- Element is explicitly hidden from accessibility (`accessible={false}`, `accessibilityElementsHidden={true}` on iOS, `importantForAccessibility="no"` on Android)
+- Element is a list item wrapper where the child component handles its own accessibility
+
+**Search Patterns** (hints for reviewers):
+- `Account Balance
+
+// Capping font scaling to prevent layout issues instead of fixing the layout
+$2,450.00
+
+// Fixed height container that clips scaled text
+
+ This text will be clipped at larger scales
+
+```
+
+### Correct
+
+```tsx
+// Text respects system scaling (default behavior — don't override it)
+Account Balance
+
+// Flexible container that accommodates scaled text
+
+
+ This text grows with the container at larger scales
+
+
+
+// Acceptable: limiting scaling on tiny decorative text only (e.g., badge count)
+3
+```
+
+---
+
+### Review Metadata
+
+Flag ONLY when ANY of these patterns is found:
+
+- `allowFontScaling={false}` on `` or `` displaying user-facing content
+- `maxFontSizeMultiplier={1}` effectively disabling scaling on readable text
+- Fixed `height` on a container with text content and no `minHeight` or overflow accommodation
+
+**DO NOT flag if:**
+
+- `allowFontScaling={false}` on purely decorative text (icons rendered as text, single-character badges)
+- `maxFontSizeMultiplier` set to a reasonable value (>= 1.5) to prevent extreme layout breakage
+- Container uses `minHeight` instead of `height`
+- Text is inside a component that manages scaling internally
+
+**Search Patterns** (hints for reviewers):
+- `allowFontScaling={false}`
+- `maxFontSizeMultiplier={1}`
+- `maxFontSizeMultiplier={1.0}`
+- Fixed `height:` on text containers
diff --git a/.claude/skills/coding-standards/rules/a11y-11-form-accessibility.md b/.claude/skills/coding-standards/rules/a11y-11-form-accessibility.md
new file mode 100644
index 0000000000000..4cdcbb398fa28
--- /dev/null
+++ b/.claude/skills/coding-standards/rules/a11y-11-form-accessibility.md
@@ -0,0 +1,90 @@
+---
+ruleId: A11Y-11
+title: Forms must have accessible labels, errors, and instructions
+---
+
+## [A11Y-11] Forms must have accessible labels, errors, and instructions
+
+### Reasoning
+
+Screen reader users navigate forms field by field. Each input must be associated with a descriptive label so users know what to enter. While React Native uses `placeholder` as a fallback accessible name, it disappears once the user starts typing, leaving the field unlabeled. An explicit `accessibilityLabel` (or `accessibilityLabelledBy` on Android) persists regardless of input state. Error messages must be announced when they appear using `accessibilityLiveRegion` (Android) and `AccessibilityInfo.announceForAccessibility()` (iOS). (WCAG 1.3.1, 3.3.1, 3.3.2)
+
+### Incorrect
+
+```tsx
+// Input with no accessible label — screen reader says "edit text"
+
+
+// Error not announced, not associated with input
+
+{emailError && {emailError}}
+```
+
+### Correct
+
+```tsx
+// Input with accessible label
+
+
+// Android: label linked to input via nativeID
+{translate('common.email')}
+
+
+// Error announced via live region (Android) + announceForAccessibility (iOS)
+
+{emailError && (
+
+ {emailError}
+
+)}
+
+// iOS: announce error to VoiceOver
+useEffect(() => {
+ if (emailError) {
+ AccessibilityInfo.announceForAccessibility(emailError);
+ }
+}, [emailError]);
+```
+
+---
+
+### Review Metadata
+
+Flag ONLY when ANY of these patterns is found:
+
+- `` with **no** `accessibilityLabel` and **no** `accessibilityLabelledBy`
+- `` relying **only** on `placeholder` for labeling (placeholder disappears once user types, leaving the field unlabeled for screen readers)
+- Form validation error text rendered without `accessibilityLiveRegion` or `accessibilityRole="alert"`
+
+**DO NOT flag if:**
+
+- Using a form component library that wraps inputs with labels internally
+- `accessibilityLabel` is set on the input or a parent `accessible` container
+- `accessibilityLabelledBy` links to a visible label via `nativeID` (Android-only — ensure `accessibilityLabel` is also set as iOS fallback)
+
+**Search Patterns** (hints for reviewers):
+- `
+ Submit
+ Cancel
+
+
+// Header visually on top via absolute positioning, but last in JSX
+
+ Body content
+
+ Title
+
+
+```
+
+### Correct
+
+```tsx
+// JSX order matches visual reading order
+
+ Cancel
+ Submit
+
+
+// Header first in JSX, matching visual order
+
+
+ Title
+
+ Body content
+
+```
+
+---
+
+### Review Metadata
+
+Flag ONLY when ANY of these patterns is found:
+
+- `flexDirection: 'row-reverse'` or `'column-reverse'` on a container with multiple interactive children
+- Absolute positioning causing an element to appear visually before its JSX siblings
+- `zIndex` layering that places interactive elements in a different visual order than JSX order
+
+**DO NOT flag if:**
+
+- `row-reverse` is used on a container with a single child or non-interactive children
+- Visual reordering is purely decorative (no interactive elements affected)
+- The component uses `experimental_accessibilityOrder` to explicitly control focus order
+
+**Search Patterns** (hints for reviewers):
+- `flexDirection: 'row-reverse'` / `'column-reverse'`
+- `position: 'absolute'` on interactive elements
+- `zIndex` on containers with multiple interactive children
diff --git a/.claude/skills/coding-standards/rules/a11y-2-semantic-roles.md b/.claude/skills/coding-standards/rules/a11y-2-semantic-roles.md
new file mode 100644
index 0000000000000..72eb299f006f2
--- /dev/null
+++ b/.claude/skills/coding-standards/rules/a11y-2-semantic-roles.md
@@ -0,0 +1,75 @@
+---
+ruleId: A11Y-2
+title: Use semantic accessibilityRole for interactive elements
+---
+
+## [A11Y-2] Use semantic accessibilityRole for interactive elements
+
+### Reasoning
+
+React Native components have no implicit semantic meaning — assistive technology treats them as generic containers. Interactive elements must declare their role via `accessibilityRole` or `role` so screen readers can convey what the element does. Note: `role` is a cross-platform alias that takes precedence over `accessibilityRole` when both are set. (WCAG 4.1.2)
+
+### Incorrect
+
+```tsx
+// Pressable with no role — screen reader doesn't convey it's a button
+
+ Submit
+
+
+// Pressable acting as link with no role
+ openURL(href)}>
+ Learn more
+
+
+// Section heading with no role
+Account Settings
+```
+
+### Correct
+
+```tsx
+
+ Submit
+
+
+ openURL(href)}
+>
+ Learn more
+
+
+
+ Account Settings
+
+```
+
+---
+
+### Review Metadata
+
+Flag ONLY when ANY of these patterns is found:
+
+- `` or interactive component with `onPress`/`onLongPress` handler but **no** `accessibilityRole` or `role` prop
+- `` or `` navigating to a URL without `accessibilityRole="link"`
+- Text styled as a heading (large/bold, section title) without `accessibilityRole="header"`
+- Toggle/switch UI without `accessibilityRole="switch"` or `"checkbox"`
+- Tab UI without `accessibilityRole="tab"`
+
+**DO NOT flag if:**
+
+- Component already has `accessibilityRole` or `role` set
+- Using a design system component that sets the role internally (e.g., `