---
name: axiom-accessibility-diag
description: Use when fixing VoiceOver issues, Dynamic Type violations, color contrast failures, touch target problems, keyboard navigation gaps, or Reduce Motion support - comprehensive accessibility diagnostics with WCAG compliance, Accessibility Inspector workflows, and App Store Review preparation for iOS/macOS
license: MIT
metadata:
  version: "1.0.0"
---

# Accessibility Diagnostics

## Overview

Systematic accessibility diagnosis and remediation for iOS/macOS apps. Covers the 7 most common accessibility issues that cause App Store rejections and user complaints.

**Core principle** Accessibility is not optional. iOS apps must support VoiceOver, Dynamic Type, and sufficient color contrast to pass App Store Review. Users with disabilities depend on these features.

## When to Use This Skill

- Fixing VoiceOver navigation issues (missing labels, wrong element order)
- Supporting Dynamic Type (text scaling for vision disabilities)
- Meeting color contrast requirements (WCAG AA/AAA)
- Fixing touch target size violations (< 44x44pt)
- Adding keyboard navigation (iPadOS/macOS)
- Supporting Reduce Motion (vestibular disorders)
- Preparing for App Store Review accessibility requirements
- Responding to user complaints about accessibility

## The 7 Critical Accessibility Issues

### 1. VoiceOver Labels & Hints (CRITICAL - App Store Rejection)

**Problem** Missing or generic accessibility labels prevent VoiceOver users from understanding UI purpose.

**WCAG** 4.1.2 Name, Role, Value (Level A)

#### Common violations
```swift
// ❌ WRONG - No label (VoiceOver says "Button")
Button(action: addToCart) {
  Image(systemName: "cart.badge.plus")
}

// ❌ WRONG - Generic label
.accessibilityLabel("Button")

// ❌ WRONG - Reads implementation details
.accessibilityLabel("cart.badge.plus") // VoiceOver: "cart dot badge dot plus"

// ✅ CORRECT - Descriptive label
Button(action: addToCart) {
  Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Add to cart")

// ✅ CORRECT - With hint for complex actions
.accessibilityLabel("Add to cart")
.accessibilityHint("Double-tap to add this item to your shopping cart")
```

#### When to use hints
- Action is not obvious from label ("Add to cart" is obvious, no hint needed)
- Multi-step interaction ("Swipe right to confirm, left to cancel")
- State change ("Double-tap to toggle notifications on or off")

#### Decorative elements
```swift
// ✅ CORRECT - Hide decorative images from VoiceOver
Image("decorative-pattern")
  .accessibilityHidden(true)

// ✅ CORRECT - Combine multiple elements into one label
HStack {
  Image(systemName: "star.fill")
  Text("4.5")
  Text("(234 reviews)")
}
.accessibilityElement(children: .combine)
.accessibilityLabel("Rating: 4.5 stars from 234 reviews")
```

#### Testing
- Enable VoiceOver: Cmd+F5 (simulator) or triple-click side button (device)
- Navigate: Swipe right/left to move between elements
- Listen: Does VoiceOver announce purpose clearly?
- Check order: Does navigation order match visual layout?

---

### 2. Dynamic Type Support (HIGH - User Experience)

**Problem** Fixed font sizes prevent users with vision disabilities from reading text.

**WCAG** 1.4.4 Resize Text (Level AA - support 200% scaling without loss of content/functionality)

#### Common violations
```swift
// ❌ WRONG - Fixed size, won't scale
Text("Price: $19.99")
  .font(.system(size: 17))

UILabel().font = UIFont.systemFont(ofSize: 17)

// ❌ WRONG - Custom font without scaling
Text("Headline")
  .font(Font.custom("CustomFont", size: 24))

// ✅ CORRECT - SwiftUI semantic styles (auto-scales)
Text("Price: $19.99")
  .font(.body)

Text("Headline")
  .font(.headline)

// ✅ CORRECT - UIKit semantic styles
label.font = UIFont.preferredFont(forTextStyle: .body)

// ✅ CORRECT - Custom font with scaling
let customFont = UIFont(name: "CustomFont", size: 24)!
label.font = UIFontMetrics.default.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true
```

#### Custom sizes that scale with Dynamic Type
```swift
// ❌ WRONG - Fixed size, won't scale
Text("Price: $19.99")
  .font(.system(size: 17))

// ⚠️ ACCEPTABLE - Custom font without scaling (accessibility violation)
Text("Headline")
  .font(Font.custom("CustomFont", size: 24))

// ✅ GOOD - Custom size that scales with Dynamic Type
Text("Large Title")
  .font(.system(size: 60).relativeTo(.largeTitle))

Text("Custom Headline")
  .font(.system(size: 24).relativeTo(.title2))

// ✅ BEST - Use semantic styles when possible
Text("Headline")
  .font(.headline)
```

**How `relativeTo:` works**
- Base size: Your exact pixel size (24pt, 60pt, etc.)
- Scales with: The text style you specify (`.title2`, `.largeTitle`, etc.)
- Result: When user increases text size in Settings, your custom size grows proportionally

**Example**
- `.title2` base: ~22pt → Your custom: 24pt (1.09x larger)
- User increases to "Extra Large" text
- `.title2` grows to ~28pt → Your custom grows to ~30.5pt (maintains 1.09x ratio)

**Fix hierarchy (best to worst)**
1. **Best**: Use semantic styles (`.title`, `.body`, `.caption`)
2. **Good**: Use `.system(size:).relativeTo()` for required custom sizes
3. **Acceptable**: Custom font with `.dynamicTypeSize()` modifier
4. **Unacceptable**: Fixed sizes that never scale

#### SwiftUI text styles
- `.largeTitle` - 34pt (scales to 44pt at accessibility sizes)
- `.title` - 28pt
- `.title2` - 22pt
- `.title3` - 20pt
- `.headline` - 17pt semibold
- `.body` - 17pt (default)
- `.callout` - 16pt
- `.subheadline` - 15pt
- `.footnote` - 13pt
- `.caption` - 12pt
- `.caption2` - 11pt

#### Layout considerations
```swift
// ❌ WRONG - Fixed frame breaks with large text
Text("Long product description...")
  .font(.body)
  .frame(height: 50) // Clips at large text sizes

// ✅ CORRECT - Flexible frame
Text("Long product description...")
  .font(.body)
  .lineLimit(nil) // Allow multiple lines
  .fixedSize(horizontal: false, vertical: true)

// ✅ CORRECT - Stack rearranges at large sizes
HStack {
  Text("Label:")
  Text("Value")
}
.dynamicTypeSize(...DynamicTypeSize.xxxLarge) // Limit maximum size if needed
```

#### Testing
1. Xcode Preview: Environment override
   ```swift
   .environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge)
   ```

2. Simulator: Settings → Accessibility → Display & Text Size → Larger Text → Drag to maximum

3. Device: Settings → Accessibility → Display & Text Size → Larger Text

4. Check: Does text remain readable? Does layout adapt? Is any text clipped?

---

### 3. Color Contrast (HIGH - Vision Disabilities)

**Problem** Low contrast text is unreadable for users with vision disabilities or in bright sunlight.

#### WCAG
- **1.4.3 Contrast (Minimum)** — Level AA
  - Normal text (< 18pt): 4.5:1 contrast ratio
  - Large text (≥ 18pt or ≥ 14pt bold): 3:1 contrast ratio
- **1.4.6 Contrast (Enhanced)** — Level AAA
  - Normal text: 7:1 contrast ratio
  - Large text: 4.5:1 contrast ratio

#### Common violations
```swift
// ❌ WRONG - Low contrast (1.8:1 - fails WCAG)
Text("Warning")
  .foregroundColor(.yellow) // on white background

// ❌ WRONG - Low contrast in dark mode
Text("Info")
  .foregroundColor(.gray) // on black background

// ✅ CORRECT - High contrast (7:1+ passes AAA)
Text("Warning")
  .foregroundColor(.orange) // or .red

// ✅ CORRECT - System colors adapt to light/dark mode
Text("Info")
  .foregroundColor(.primary) // Black in light mode, white in dark

Text("Secondary")
  .foregroundColor(.secondary) // Automatic high contrast
```

#### Differentiate Without Color
```swift
// ❌ WRONG - Color alone indicates status
Circle()
  .fill(isAvailable ? .green : .red)

// ✅ CORRECT - Color + icon/text
HStack {
  Image(systemName: isAvailable ? "checkmark.circle.fill" : "xmark.circle.fill")
  Text(isAvailable ? "Available" : "Unavailable")
}
.foregroundColor(isAvailable ? .green : .red)

// ✅ CORRECT - Respect system preference
if UIAccessibility.shouldDifferentiateWithoutColor {
  // Use patterns, icons, or text instead of color alone
}
```

#### Testing
1. Use Color Contrast Analyzer tool (free download)
2. Screenshot your UI, measure text vs background
3. Check both light and dark mode
4. Settings → Accessibility → Display & Text Size → Increase Contrast (test with this ON)

#### Quick reference
- Black (#000000) on White (#FFFFFF): 21:1 ✅ AAA
- Dark Gray (#595959) on White: 7:1 ✅ AAA
- Medium Gray (#767676) on White: 4.5:1 ✅ AA
- Light Gray (#959595) on White: 2.8:1 ❌ Fails

---

### 4. Touch Target Sizes (MEDIUM - Motor Disabilities)

**Problem** Small tap targets are difficult or impossible for users with motor disabilities.

**WCAG** 2.5.5 Target Size (Level AAA - 44x44pt minimum)

**Apple HIG** 44x44pt minimum for all tappable elements

#### Common violations
```swift
// ❌ WRONG - Too small (24x24pt)
Button("×") {
  dismiss()
}
.frame(width: 24, height: 24)

// ❌ WRONG - Small icon without padding
Image(systemName: "heart")
  .font(.system(size: 16))
  .onTapGesture { }

// ✅ CORRECT - Minimum 44x44pt
Button("×") {
  dismiss()
}
.frame(minWidth: 44, minHeight: 44)

// ✅ CORRECT - Larger icon or padding
Image(systemName: "heart")
  .font(.system(size: 24))
  .frame(minWidth: 44, minHeight: 44)
  .contentShape(Rectangle()) // Expand tap area
  .onTapGesture { }

// ✅ CORRECT - UIKit button with edge insets
button.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
// Total size: icon size + insets ≥ 44x44pt
```

#### Spacing between targets
```swift
// ❌ WRONG - Targets too close (hard to tap accurately)
HStack(spacing: 4) {
  Button("Edit") { }
  Button("Delete") { }
}

// ✅ CORRECT - Adequate spacing (8pt minimum, 12pt better)
HStack(spacing: 12) {
  Button("Edit") { }
  Button("Delete") { }
}
```

#### Testing
1. Accessibility Inspector: Xcode → Open Developer Tool → Accessibility Inspector
2. Select "Audit" tab → Run audit → Check for "Small Text" and "Hit Region" warnings
3. Manual: Tap with one finger (not stylus) — can you hit it reliably without mistakes?

---

### 5. Keyboard Navigation (MEDIUM - iPadOS/macOS)

**Problem** Users who cannot use touch/mouse cannot navigate app.

**WCAG** 2.1.1 Keyboard (Level A - all functionality available via keyboard)

#### Common violations
```swift
// ❌ WRONG - Custom gesture without keyboard alternative
.onTapGesture {
  showDetails()
}
// No way to trigger with keyboard

// ✅ CORRECT - Button provides keyboard support automatically
Button("Show Details") {
  showDetails()
}
.keyboardShortcut("d", modifiers: .command) // Optional shortcut

// ✅ CORRECT - Custom control with focus support
struct CustomButton: View {
  @FocusState private var isFocused: Bool

  var body: some View {
    Text("Custom")
      .focusable()
      .focused($isFocused)
      .onKeyPress(.return) {
        action()
        return .handled
      }
  }
}
```

#### Focus management
```swift
// ✅ CORRECT - Set initial focus
.focusSection() // Group related controls
.defaultFocus($focus, .constant(true)) // Set default

// ✅ CORRECT - Move focus after action
@FocusState private var focusedField: Field?

Button("Next") {
  focusedField = .next
}
```

#### Testing (iPadOS/macOS)
1. Connect keyboard to iPad or use Mac
2. Press Tab - does focus move to interactive elements?
3. Press Space/Return - does focused element activate?
4. Check custom controls have visible focus indicator
5. Can you reach all functionality without mouse/touch?

---

### 6. Reduce Motion Support (MEDIUM - Vestibular Disorders)

**Problem** Animations cause discomfort, nausea, or seizures for users with vestibular disorders.

**WCAG** 2.3.3 Animation from Interactions (Level AAA - motion animation can be disabled)

#### Common violations
```swift
// ❌ WRONG - Always animates (can cause nausea)
.onAppear {
  withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
    scale = 1.0
  }
}

// ❌ WRONG - Parallax scrolling without opt-out
ScrollView {
  GeometryReader { geo in
    Image("hero")
      .offset(y: geo.frame(in: .global).minY * 0.5) // Parallax
  }
}

// ✅ CORRECT - Respect Reduce Motion preference
.onAppear {
  if UIAccessibility.isReduceMotionEnabled {
    scale = 1.0 // Instant
  } else {
    withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
      scale = 1.0
    }
  }
}

// ✅ CORRECT - Simpler animation or cross-fade
if UIAccessibility.isReduceMotionEnabled {
  // Cross-fade or instant change
  withAnimation(.linear(duration: 0.2)) {
    showView = true
  }
} else {
  // Complex spring animation
  withAnimation(.spring()) {
    showView = true
  }
}
```

#### SwiftUI modifier
```swift
// ✅ CORRECT - Automatic support
.animation(.spring(), value: isExpanded)
.transaction { transaction in
  if UIAccessibility.isReduceMotionEnabled {
    transaction.animation = nil // Disable animation
  }
}
```

#### Testing
1. Settings → Accessibility → Motion → Reduce Motion (toggle ON)
2. Navigate app - are animations reduced or eliminated?
3. Test: Transitions, scrolling effects, parallax, particle effects
4. Video autoplay should also respect this preference

---

### 7. Common Violations (HIGH - App Store Review)

#### Images Without Labels

```swift
// ❌ WRONG - Informative image without label
Image("product-photo")

// ✅ CORRECT - Informative image with label
Image("product-photo")
  .accessibilityLabel("Red sneakers with white laces")

// ✅ CORRECT - Decorative image hidden
Image("background-pattern")
  .accessibilityHidden(true)
```

#### Buttons With Wrong Traits

```swift
// ❌ WRONG - Custom button without button trait
Text("Submit")
  .onTapGesture {
    submit()
  }
// VoiceOver announces as "Submit, text" not "Submit, button"

// ✅ CORRECT - Use Button for button-like controls
Button("Submit") {
  submit()
}
// VoiceOver announces as "Submit, button"

// ✅ CORRECT - Custom control with correct trait
Text("Submit")
  .accessibilityAddTraits(.isButton)
  .onTapGesture {
    submit()
  }
```

#### Inaccessible Custom Controls

```swift
// ❌ WRONG - Custom slider without accessibility support
struct CustomSlider: View {
  @Binding var value: Double

  var body: some View {
    // Drag gesture only, no VoiceOver support
    GeometryReader { geo in
      // ...
    }
    .gesture(DragGesture()...)
  }
}

// ✅ CORRECT - Custom slider with accessibility actions
struct CustomSlider: View {
  @Binding var value: Double

  var body: some View {
    GeometryReader { geo in
      // ...
    }
    .gesture(DragGesture()...)
    .accessibilityElement()
    .accessibilityLabel("Volume")
    .accessibilityValue("\(Int(value))%")
    .accessibilityAdjustableAction { direction in
      switch direction {
      case .increment:
        value = min(value + 10, 100)
      case .decrement:
        value = max(value - 10, 0)
      @unknown default:
        break
      }
    }
  }
}
```

#### Missing State Announcements

```swift
// ❌ WRONG - State change without announcement
Button("Toggle") {
  isOn.toggle()
}

// ✅ CORRECT - State change with announcement
Button("Toggle") {
  isOn.toggle()
  UIAccessibility.post(
    notification: .announcement,
    argument: isOn ? "Enabled" : "Disabled"
  )
}

// ✅ CORRECT - Automatic state with accessibilityValue
Button("Toggle") {
  isOn.toggle()
}
.accessibilityValue(isOn ? "Enabled" : "Disabled")
```

## 8. Assistive Access Support (iOS 17+ — Cognitive Disabilities)

**Problem** App is unavailable or broken in Assistive Access mode, excluding users with cognitive disabilities who rely on a simplified system experience.

Assistive Access is a system-wide mode (Settings > Accessibility > Assistive Access) that replaces the standard iOS UI with large controls, simplified navigation, and reduced cognitive load. Apps that don't opt in are hidden from users in this mode.

#### Symptom: App missing from Assistive Access home screen

Your app doesn't appear under "Optimized Apps" in Assistive Access settings.

```xml
<!-- ✅ FIX - Add to Info.plist -->
<key>UISupportsAssistiveAccess</key>
<true/>
```

This makes the app available and launches it full screen in Assistive Access mode. Without this key, users in Assistive Access mode cannot access your app at all.

#### Symptom: Standard UI too complex for Assistive Access users

Your app launches in Assistive Access but shows the full standard interface, overwhelming users who need simplified controls.

```swift
// ✅ FIX - Provide a dedicated Assistive Access scene
@main
struct MyApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView() // Standard UI
    }

    AssistiveAccess {
      AssistiveAccessContentView() // Simplified UI
    }
  }
}
```

The `AssistiveAccess` scene type provides a separate entry point. When the system is in Assistive Access mode, it uses this scene instead of the standard `WindowGroup`. Native SwiftUI controls inside this scene automatically adopt the Assistive Access visual style (large buttons, prominent navigation, grid/row layout).

#### Symptom: App already designed for cognitive accessibility but displays in reduced frame

If your app is already purpose-built for users with cognitive disabilities (e.g., AAC apps), it may appear in a reduced frame rather than full screen.

```xml
<!-- ✅ FIX - Add to Info.plist for apps already designed for cognitive accessibility -->
<key>UISupportsFullScreenInAssistiveAccess</key>
<true/>
```

This displays your app identically to its standard appearance, bypassing the Assistive Access frame.

#### Detecting Assistive Access at runtime

```swift
struct MyView: View {
  @Environment(\.accessibilityAssistiveAccessEnabled) var assistiveAccessEnabled

  var body: some View {
    if assistiveAccessEnabled {
      // Simplified content
    } else {
      // Standard content
    }
  }
}
```

#### UIKit implementation

For UIKit apps, use the `.windowAssistiveAccessApplication` scene session role in your `UISceneConfiguration` to route to a dedicated scene delegate for the Assistive Access experience.

#### Design principles for Assistive Access scenes

- **Distill to core functionality** — One or two essential features, not the full app
- **Large, prominent controls** — Ample spacing, no hidden gestures or timed interactions
- **Multiple representations** — Pair text with icons; use visual alternatives
- **Step-by-step navigation** — Clear back buttons, consistent patterns
- **Safe interactions** — Remove irreversible actions; confirm destructive ones

#### Adding navigation icons

```swift
NavigationStack {
  MyView()
    .navigationTitle("My Feature")
    .assistiveAccessNavigationIcon(systemImage: "star.fill")
}
```

#### Testing

1. **Device** — Enable Assistive Access in Settings > Accessibility > Assistive Access, verify app appears in "Optimized Apps", test the full user flow
2. **Accessibility Inspector** — Run audit on the Assistive Access scene for label, contrast, and hit region issues

---

## Accessibility Inspector Workflow

### 1. Launch Accessibility Inspector

Xcode → Open Developer Tool → Accessibility Inspector

### 2. Select Target

- Dropdown: Choose running simulator or connected device
- Target: Select your app

### 3. Inspection Mode

- Click "Inspection Pointer" button (crosshair icon)
- Hover over UI elements to see:
  - Label, Value, Hint, Traits
  - Frame, Path
  - Actions available
  - Parent/child hierarchy

### 4. Run Audit

- Click "Audit" tab
- Click "Run Audit" button
- Review findings:
  - **Contrast** — Color contrast issues
  - **Hit Region** — Touch target size issues
  - **Clipped Text** — Text truncation with Dynamic Type
  - **Element Description** — Missing labels/hints
  - **Traits** — Wrong accessibility traits

### 5. Fix and Re-Test

- Click each finding for details
- Fix in code
- Re-run audit to verify

## VoiceOver Testing Checklist

### Enable VoiceOver
- **Simulator** Cmd+F5 or Settings → Accessibility → VoiceOver
- **Device** Triple-click side button (if enabled in Settings)

### Navigation Testing
1. ☐ Swipe right/left - moves logically through UI elements
2. ☐ Each element announces purpose clearly
3. ☐ No unlabeled elements (except decorative)
4. ☐ Heading navigation works (swipe up/down with 2 fingers)
5. ☐ Container navigation works (swipe left/right with 3 fingers)

### Interaction Testing
1. ☐ Double-tap activates buttons
2. ☐ Swipe up/down adjusts sliders/pickers (with `.accessibilityAdjustableAction`)
3. ☐ Custom gestures have VoiceOver equivalents
4. ☐ Text fields announce keyboard type
5. ☐ State changes are announced

### Content Testing
1. ☐ Images have descriptive labels or are hidden
2. ☐ Error messages are announced
3. ☐ Loading states are announced
4. ☐ Modal sheets announce role
5. ☐ Alerts announce automatically

## App Store Review Preparation

### Required Accessibility Features (iOS)

1. **VoiceOver Support**
   - All UI elements must have labels
   - Navigation must be logical
   - All actions must be performable

2. **Dynamic Type**
   - Text must scale from -3 to +12 sizes
   - Layout must adapt without clipping

3. **Sufficient Contrast**
   - Minimum 4.5:1 for normal text
   - Minimum 3:1 for large text (≥18pt)

### App Store Connect Metadata

When submitting:
1. Accessibility → Select features your app supports:
   - ☑ VoiceOver
   - ☑ Dynamic Type
   - ☑ Increased Contrast
   - ☑ Reduce Motion (if supported)

2. Test Notes: Document accessibility testing
   ```
   Accessibility Testing Completed:
   - VoiceOver: All screens tested with VoiceOver enabled
   - Dynamic Type: Tested at all size categories
   - Color Contrast: Verified 4.5:1 minimum contrast
   - Touch Targets: All buttons minimum 44x44pt
   - Reduce Motion: Animations respect user preference
   ```

### Common Rejection Reasons

1. **"App is not fully functional with VoiceOver"**
   - Missing labels on images/buttons
   - Unlabeled custom controls
   - Actions not performable with VoiceOver

2. **"Text is not readable at all Dynamic Type sizes"**
   - Fixed font sizes
   - Text clipping at large sizes
   - Layout breaks at accessibility sizes

3. **"Insufficient color contrast"**
   - Text fails 4.5:1 ratio
   - UI elements fail 3:1 ratio
   - Color-only indicators

---

## Design Review Pressure: Defending Accessibility Requirements

### The Problem

Under design review pressure, you'll face requests to:
- "Those VoiceOver labels make the code messy - can we skip them?"
- "Dynamic Type breaks our carefully designed layout - let's lock font sizes"
- "The high contrast requirement ruins our brand aesthetic"
- "44pt touch targets are too big - make them smaller for a cleaner look"

These sound like reasonable design preferences. **But they violate App Store requirements and exclude 15% of users.** Your job: defend using App Store guidelines and legal requirements, not opinion.

### Red Flags — Designer Requests That Violate Accessibility

If you hear ANY of these, **STOP and reference this skill**:

- ❌ **"Skip VoiceOver labels on icon-only buttons"** – App Store rejection (Guideline 2.5.1)
- ❌ **"Use fixed 14pt font for compact design"** – Excludes users with vision disabilities
- ❌ **"3:1 contrast ratio is fine"** – Fails WCAG AA for text (needs 4.5:1)
- ❌ **"Make buttons 36x36pt for clean aesthetic"** – Fails touch target requirement (44x44pt minimum)
- ❌ **"Disable Dynamic Type in this screen"** – App Store rejection risk
- ❌ **"Color-code without labels (red=error, green=success)"** – Excludes colorblind users (8% of men)

### How to Push Back Professionally

#### Step 1: Show the Guideline

```
"I want to support this design direction, but let me show you Apple's App Store
Review Guideline 2.5.1:

'Apps should support accessibility features such as VoiceOver and Dynamic Type.
Failure to include sufficient accessibility features may result in rejection.'

Here's what we need for approval:
1. VoiceOver labels on all interactive elements
2. Dynamic Type support (can't lock font sizes)
3. 4.5:1 contrast ratio for text, 3:1 for UI
4. 44x44pt minimum touch targets

Let me show where our design currently falls short..."
```

#### Step 2: Demonstrate the Risk

Open the app with accessibility features enabled:
- **VoiceOver** (Cmd+F5): Show buttons announcing "Button" instead of purpose
- **Largest Text Size**: Show layout breaking or text clipping
- **Color Contrast Analyzer**: Show failing contrast ratios
- **Touch target overlay**: Show targets < 44pt

#### Reference
- App Store Review Guideline 2.5.1
- WCAG 2.1 Level AA (industry standard)
- ADA compliance requirements (legal risk in US)

#### Step 3: Offer Compromise

```
"I can achieve your aesthetic goals while meeting accessibility requirements:

1. VoiceOver labels: Add them programmatically (invisible in UI, required for approval)
2. Dynamic Type: Use layout techniques that adapt (examples from Apple HIG)
3. Contrast: Adjust colors slightly to meet 4.5:1 (I'll show options that preserve brand)
4. Touch targets: Expand hit areas programmatically (visual size stays the same)

These changes won't affect the visual design you're seeing, but they're required
for App Store approval and legal compliance."
```

#### Step 4: Document the Decision

If overruled (designer insists on violations):

```
Slack message to PM + designer:

"Design review decided to proceed with:
- Fixed font sizes (disabling Dynamic Type)
- 38x38pt buttons (below 44pt requirement)
- 3.8:1 text contrast (below 4.5:1 requirement)

Important: These changes violate App Store Review Guideline 2.5.1 and WCAG AA.
This creates three risks:

1. App Store rejection during review (adds 1-2 week delay)
2. ADA compliance issues if user files complaint (legal risk)
3. 15% of potential users unable to use app effectively

I'm flagging this proactively so we can prepare a response plan if rejected."
```

#### Why this works
- You're not questioning their design taste
- You're raising App Store rejection risk (business impact)
- You're citing specific guidelines (not opinion)
- You're offering solutions that preserve visual design
- You're documenting the decision (protects you post-rejection)

### Real-World Example: App Store Rejection (48-Hour Resubmit Window)

#### Scenario
- 48 hours until resubmit deadline after rejection
- Apple cited: "2.5.1 - Insufficient VoiceOver support"
- Designer says: "Just add generic labels quickly"
- PM watching the meeting, wants fastest fix

#### What to do

```swift
// ❌ WRONG - Generic labels (will fail re-review)
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Button") // Apple will reject again

// ✅ CORRECT - Descriptive labels (passes review)
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Add to cart")
.accessibilityHint("Double-tap to add this item to your shopping cart")
```

#### In the meeting, demonstrate
1. Enable VoiceOver (Cmd+F5)
2. Show "Button" announcement (generic - fails)
3. Show "Add to cart" announcement (descriptive - passes)
4. Reference Apple's rejection message: "Elements must have descriptive labels"

**Time estimate** 2-4 hours to audit all interactive elements and add proper labels.

#### Result
- Honest time estimate prevents second rejection
- Proper labels pass Apple review
- Resubmit accepted within 48 hours

### When to Accept the Design Decision (Even If You Disagree)

Sometimes designers have valid reasons to override accessibility guidelines. Accept if:

- [ ] They understand the App Store rejection risk
- [ ] They're willing to delay launch if rejected
- [ ] You document the decision in writing
- [ ] They commit to fixing if rejected

#### Document in Slack

```
"Design review decided to proceed with [specific violations].

We understand this creates:
- App Store rejection risk (Guideline 2.5.1)
- Potential 1-2 week delay if rejected
- Need to audit and fix all instances if rejected

Monitoring plan:
- Submit for review with current design
- If rejected, implement proper accessibility (estimated 2-4 hours)
- Have accessibility-compliant version ready as backup"
```

This protects both of you and shows you're not blocking - just de-risking.

---

## WCAG Compliance Levels

### Level A (Minimum — Required for App Store)
- 1.1.1 Non-text Content — Images have text alternatives
- 2.1.1 Keyboard — All functionality via keyboard (iPadOS/macOS)
- 4.1.2 Name, Role, Value — Elements have accessible names

### Level AA (Standard — Recommended)
- 1.4.3 Contrast (Minimum) — 4.5:1 text, 3:1 UI
- 1.4.4 Resize Text — Support 200% text scaling
- 1.4.5 Images of Text — Use real text when possible

### Level AAA (Enhanced — Best Practice)
- 1.4.6 Contrast (Enhanced) — 7:1 text, 4.5:1 UI
- 2.3.3 Animation from Interactions — Reduce Motion support
- 2.5.5 Target Size - 44x44pt minimum targets

**Goal** Meet Level AA for all content, Level AAA where feasible.

## Quick Command Reference

After making fixes:

```bash
# Quick scan for new issues
/axiom:audit-accessibility

# Deep diagnosis for specific issues
/skill axiom:accessibility-diag
```

## Resources

**Docs**: /accessibility/voiceover, /uikit/uifont/scaling_fonts_automatically

---

**Remember** Accessibility is not a feature, it's a requirement. 15% of users have some form of disability. Making your app accessible isn't just the right thing to do - it expands your user base and improves the experience for everyone.
