rebreak-monorepo/ops/ACCESSIBILITY_AUDIT.md
chahinebrini 5d6c322129 wip: KeyboardAwareSheet migrations + Snake/Tetris UI + iron.png + useMe live-update
Sheets via neuer KeyboardAwareSheet-Composable (in Modal pattern, auto-grow
mit Tastatur, paddingBottom-Lift): EditMail, AddDomain, CreateRoom, ConnectMail.
GameOverScreen behält Spring-Slide-In, nutzt RN Keyboard.addListener für Lift.

- KeyboardAwareSheet.tsx — universal modal with sheet-grow + keyboard-padding
- react-native-keyboard-controller installiert + KeyboardProvider in Root
- Snake: time + ScoreProgressBar + useSnakeSounds (haptic, audio TODO)
- Tetris: title weg, Buttons zentriert, kein Pressable mit style-fn
- DPad-Buttons 60→48, more bg, no scale
- useMe: pub-sub listener pattern für app-weite avatar/nickname-Updates
- dm.tsx: resolveAvatar wrap (iron.png-Warning)
- Mail-error-humanizer + locales

Recovery-Doc-Update in docs/internal/RECOVERY_LOG_2026-05-10.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:59:25 +02:00

433 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Rebreak-Native — Accessibility Audit & DiGA-Roadmap
Author: Ahmed (QA) · Stand: 2026-05-07 · Status: Initial Audit, READ-ONLY
User-Trigger 2026-05-08: „Thema accessibility auf beide Plattformen checken — es gibt
Test-Frameworks dafür. Damit können wir bei DiGA punkten."
Scope dieses Dokuments:
1. Bestandsaufnahme der A11y-Awareness im rebreak-native-Code (iOS + Android)
2. Mapping auf WCAG 2.1 Level AA + DiGA-Anforderungen
3. Test-Framework-Empfehlung (RN-spezifisch)
4. Roadmap Pre-TestFlight / Pre-DiGA-Antrag / Post-Launch
---
## 1. Executive Summary
**Aktuelle A11y-Coverage in rebreak-native: ~1,3 %.**
Empirisch: 6 Treffer für `accessibilityLabel|accessibilityRole|accessibilityHint|accessibilityState|accessible=`-Props
verteilt auf 5 Files (Mail-Add-Account, ProtectionLockedCard, AppHeader-Back-Button,
ProtectionCard-Settings-Icon, DomainGrid-Add-Btn + DomainGrid-State).
Demgegenüber im selben Tree: **453 Touchable-Komponenten** (`Pressable` /
`TouchableOpacity` / `<Button`) und **50 TextInputs** verteilt auf **83 tsx-Files**
also faktisch **>97 % unbeschriftet** für Screen-Reader (VoiceOver / TalkBack).
**DiGA-Risk-Score: HOCH.**
Begründung:
- BfArM verlangt für DiGA-Zertifizierung die Erfüllung von **WCAG 2.1 Level AA**
(DVG §139e, BIK BITV-konformes Verfahren). Heute erfüllt rebreak-native diese
Stufe **auf keiner Seite**.
- Recovery-User-Kohorte hat überdurchschnittlich oft Komorbiditäten:
Sehbeeinträchtigung (Diabetes, Augenleiden), motorische Einschränkungen,
starke kognitive Last in Krisen-Momenten — d.h. der **SOS-Flow ist
a11y-mission-critical**, und der ist heute komplett unzugänglich für Screen-Reader.
- Apple App-Review (für External Beta) und Google Play prüfen seit 2024 bei
Mental-Health-/Health-Apps zunehmend systematisch auf VoiceOver/TalkBack-Walkthrough.
**Empfehlung:** Sofortmaßnahme für SOS-Flow + Auth-Flow + Demographics-Form vor
TestFlight Internal (Wochenend-Cutover). Roadmap-Arbeit für Vollabdeckung pre-DiGA.
---
## 2. Component-by-Component-Status (Critical Paths)
Legende: ✅ ausreichend / ⚠️ teilweise / 🔴 fehlt komplett
### 2.1 SOS-Flow — `app/urge.tsx` (1333 Lines) — 🔴
Höchste Priorität (Krisen-Use-Case, Recovery-Schutz).
| Element | Line | A11y-Status | Finding |
| ----------------------------------------------------- | ------ | ----------- | ----------------------------------------------------------------------- |
| Exit-Button (Pressable + Icon-only) | 1094 | 🔴 | kein `accessibilityLabel`, ScreenReader liest „Button" (oder schweigt) |
| TTS-Stop-Button | 1116 | 🔴 | kein Label, kein Hint, keine `accessibilityState={{busy: …}}` |
| Sound-Toggle-Button | 1123 | 🔴 | toggled `soundEnabled` ohne `accessibilityState={{checked}}` |
| Scroll-Down-Button | 1160 | 🔴 | nur Icon |
| Lyra-Chip-Button (`handleChip`) | 1209 | 🔴 | kritisch: das sind die SOS-Action-Chips (Atem/Spiel/Cooldown), Screen-Reader liest gar nichts |
| Eingabefeld TextInput | 1231 | 🔴 | kein `accessibilityLabel` |
| Send-Button (Pressable, mit Disabled-State) | 1244 | 🔴 | `disabled={thinking || !input.trim()}` — Screen-Reader bekommt kein `accessibilityState={{disabled}}` |
**Verdict:** SOS-Flow ist für sehbehinderte User nicht bedienbar. Absoluter
DiGA-Blocker.
### 2.2 SOS-Spiele — `components/urge/UrgeGames.tsx` (1067 Lines) — 🔴
Wir haben gerade native D-Pad-Buttons gemacht (Snake) und Tetris-Action-Buttons.
**Keiner** hat `accessibilityLabel` oder `accessibilityRole`.
| Element | Line | Finding |
| ------------------------------------ | ---- | -------------------------------------------------------------------------- |
| Game-Picker-Pressable | 37 | 🔴 ohne Label |
| Snake-D-Pad-Btn (Up/Down/Left/Right) | 360 | 🔴 reine Icon-Pressable, Direction-Info komplett unsichtbar für VoiceOver |
| Tetris-Action-Btn (Rotate/Drop) | 405 | ⚠️ hat sichtbares Label aber kein explicit `accessibilityLabel` |
| Memory-Card-Pressable | 541 | 🔴 ohne Label, kein State-Info „revealed/matched" |
| Abandon-Button | 292 | 🔴 ohne Label |
| Replay/Continue-Buttons | 724 | ⚠️ haben Text-Inhalt — aber `accessibilityRole="button"` fehlt |
**Verdict:** Snake/Tetris/Memory/RPS sind faktisch reine Sehende-Spiele. Für
DiGA-Recovery-Effektmessung problematisch (a11y-User können die Distraktions-
Mechanik nicht nutzen).
### 2.3 Profile + Demographics — `app/profile/index.tsx` + `components/profile/DemographicsAccordion.tsx` (272 / 621 Lines) — 🔴
DiGA-Pflicht-Daten-Erhebung (Phase C).
| Element | Line | Finding |
| ------------------------------------------------ | -------- | ------------------------------------------------------------------------------ |
| TextInput Geburtsjahr | 267 | 🔴 kein Label, nur Placeholder „z.B. 1989" — nicht für VoiceOver lesbar |
| SelectButton Geschlecht | 296 | 🔴 nicht als „Combobox/Spinner" markiert, Screen-Reader nennt keine Auswahl |
| TextInput Beruf | 304 | 🔴 wie Geburtsjahr |
| SelectButton Familienstand | 320 | 🔴 |
| SelectButton Bundesland | 328 | 🔴 |
| TextInput Stadt | 336 | 🔴 |
| Modal-Picker (`onSelect(value)`) | 587 | 🔴 jeder Picker-Eintrag ist `Pressable` ohne Label → VoiceOver liest gar nichts |
| Revoke-Consent-Pressable | 351 | 🔴 wichtig für DSGVO Art. 7(3) |
| Modal-Backdrop | 539 | 🔴 fehlt `accessibilityViewIsModal` (iOS) / Focus-Trap |
**Verdict:** Gerade diese Form ist DSGVO Art. 9 + DiGA-Datensatz-Pflicht. Wenn
ein blinder User die Demographics nicht ausfüllen kann, **fehlen seine Daten in
der DiGA-Versorgungs-Studie** (Bias).
### 2.4 Header + Dropdown — `components/AppHeader.tsx` + `components/header/HeaderDropdownMenu.tsx` — ⚠️
| Element | Line | Finding |
| ---------------------------------- | ---------- | ---------------------------------------------------- |
| Back-Button | 56 | ✅ `accessibilityLabel="Zurück"` |
| Notification-Bell-Pressable | 72 | 🔴 kein Label, Badge-Count gar nicht angesagt |
| Avatar/Dropdown-Trigger | 88 | 🔴 kein Label „Profilmenü öffnen" |
| Dropdown Backdrop-Pressable | 86 (Menu) | 🔴 |
| Dropdown Items (Profile/Settings/Logout) | 108239 | 🔴 keine Labels, Modal hat kein `accessibilityViewIsModal` |
### 2.5 ComposeCard — `components/ComposeCard.tsx` — ⚠️
hitSlop ≥44pt korrekt umgesetzt (Apple-HIG-konform), aber **keine** a11y-Labels:
| Element | Line | Finding |
| ----------------------------- | ---- | -------------------------------------------------- |
| TextInput Compose | 110 | 🔴 nur Placeholder, kein Label |
| Image-Remove-Pressable | 130 | 🔴 nur Close-Icon |
| Image-Picker-Pressable | 147 | 🔴 Text „Foto" nur visuell |
| Cancel-Pressable | 160 | ⚠️ hat Text-Child, aber Role/Label-Mapping unklar |
| Share-Submit-Pressable | 168 | 🔴 disabled-State nicht annonciert |
### 2.6 Blocker — `app/(app)/blocker.tsx` + `components/blocker/*.tsx` — ⚠️ (best-of)
Hier ist mit Abstand die meiste a11y-Awareness — Schutz-Settings sind DiGA-
mission-critical und das ist hier korrekt erkannt:
| Element | A11y-Status | Note |
| -------------------------------------- | ----------- | --------------------------------------------- |
| ProtectionCard Settings-Icon | ✅ | `accessibilityLabel={t('blocker.protection_settings_a11y')}` |
| ProtectionLockedCard Settings-Icon | ✅ | dito |
| DomainGrid Add-Domain-Pressable | ✅ | Label + `accessibilityState={{disabled}}` |
| Switch (LayerSwitchCard, ProtectionCard) | ⚠️ | RN-Switch hat default `accessibilityRole="switch"` aber kein i18n-Label |
| ProtectionDetailsSheet | 🔴 | Modal ohne `accessibilityViewIsModal` |
| AddDomainSheet TextInput | 🔴 | kein Label |
| CooldownBanner | 🔴 | Animation, kein `accessibilityLiveRegion="polite"` |
| DeactivationExplainerSheet | 🔴 | Modal-Pattern wie oben |
### 2.7 Auth-Flow — `app/(auth)/{signin,signup,forgot-password,confirm,confirm-otp,device-limit}.tsx` — 🔴
Zugang zur App = a11y-Pflicht-Pfad.
| Element (signin) | Line | Finding |
| ----------------------------- | ---- | ---------------------------------------------- |
| OAuth-Google-Btn | 103 | 🔴 nur Icon-+-Text-Children, kein expliziter Label |
| OAuth-Apple-Btn | 117 | 🔴 dito |
| TextInput Email | 139 | ✅ `autoComplete="email"` + ⚠️ kein expliziter `accessibilityLabel` |
| TextInput Password | 151 | ✅ `autoComplete="password"` + ⚠️ kein Label |
| Forgot-Password-Pressable | 162 | 🔴 |
| Submit-Button | 173 | 🔴 disabled-State nicht annonciert |
| Signup-Link-Pressable | 186 | 🔴 |
`autoComplete` hilft Password-Manager, ersetzt aber **kein** `accessibilityLabel`.
### 2.8 Community / PostCard / PostCommentsSheet — 🔴
`components/PostCard.tsx`: hitSlop ≥44pt korrekt, aber Like-/Comment-Buttons
ohne Label. Like-Count (`localCount`) wird visuell angezeigt aber nicht in
`accessibilityValue` exposed → Screen-Reader liest nur „Button".
### 2.9 Animation & Reduce-Motion — 🔴 (Cross-cutting)
- **270 Animation/Reanimated-Usages** im Code (`Animated.*` / `FadeIn` /
`useNativeDriver` / `useSharedValue`).
- **0 Treffer** für `AccessibilityInfo.isReduceMotionEnabled` /
`useReduceMotion()`.
WCAG 2.3.3 (Animation from Interactions, AAA) und 2.2.2 (Pause/Stop/Hide, AA) sind
heute pauschal verletzt — die App respektiert Systemeinstellung „Bewegung
reduzieren" nicht. Für vestibuläre Empfindlichkeit problematisch.
### 2.10 Dynamic-Type / Font-Scaling — ⚠️ (Cross-cutting)
- 398 explizite `fontSize:` / `font-size`-Vorkommen im Code.
- 0 `allowFontScaling={false}` Bypässe (gut!) — RN skaliert per default mit
iOS Dynamic-Type / Android-Font-Scale.
- ABER: viele Layouts nutzen `fontSize` als hartcodierten Pixel — bei extremem
Font-Scale (Accessibility Sizes XXX-Large) brechen die Layouts wahrscheinlich.
### 2.11 Color-Contrast — ⚠️ (Cross-cutting)
Häufige Hex-Codes aus Codebase:
- `#a3a3a3` (neutral-400) auf `#ffffff` → contrast-ratio **2,84:1** → **fail
WCAG AA** (4.5:1 für Body-Text)
- `#737373` (neutral-500) auf `#ffffff` → contrast-ratio **4,48:1** → **fail
WCAG AA** (knapp; Norm fordert 4,5:1)
- `placeholderTextColor="#a3a3a3"` (signin Lines 143, 155) → fail
→ Alle „muted" / „placeholder" Texte erfüllen WCAG AA nicht. Screenshot-
basierter axe-Audit würde dutzende Findings melden.
### 2.12 Screen-Reader-Detection — 🔴 (Cross-cutting)
`grep AccessibilityInfo|isScreenReaderEnabled` → 0 Treffer.
Heißt: keine Komponente verhält sich anders, wenn Screen-Reader an ist
(z.B. Auto-Play-Audio von Lyra-TTS bei aktivem VoiceOver = problematisch,
wenn beide gleichzeitig sprechen).
### 2.13 Touch-Target-Size — ⚠️
- iOS HIG verlangt 44×44pt → vielfach via `hitSlop=12` nachträglich erfüllt
(gut: ComposeCard, PostCard).
- Android Material verlangt **48×48dp** → heutige Mehrheit der Buttons ist
hitSlop=12 → **44pt erreicht aber 48dp Android-Norm fehlt knapp**.
---
## 3. Test-Framework-Empfehlung
### 3.1 Was es gibt für RN
| Tool | Was es kann | Empfehlung |
| ------------------------------------------ | ----------------------------------------------------------------------------- | ---------- |
| `@testing-library/react-native` (RNTL) | `getByA11yLabel`, `getByRole`, `getByA11yState` — Component-Test-Assertions | **JA — Pflicht** |
| `@testing-library/jest-native` | Custom Matchers `toBeAccessible`, `toHaveAccessibilityValue` | **JA** |
| `react-native-accessibility-engine` (Meta) | DEV-time Audit-Output beim Render — gibt Warnings für fehlende Labels | **JA, Phase 2** |
| `axe-core-react-native` | Programmatic axe-Engine-Run gegen Component-Tree | **Optional**, instabil für SDK 53 |
| Maestro | E2E — kann `id: "<accessibilityLabel>"` als Selektor → indirekt a11y-Test | **JA** |
| Apple Accessibility Inspector (Xcode) | manuelle Audit-Tour mit Audit-Button | **Pflicht**, manuell pre-Release |
| Android Accessibility Scanner (Play Store) | manueller Audit über App, gibt Findings-Report | **Pflicht**, manuell pre-Release |
| BIK BITV-Test (DE) | offizieller deutscher BITV-Test-Bericht — DiGA-konform | **Pflicht für DiGA-Antrag**, externer Provider |
### 3.2 Empfehlung in einem Satz
**`jest-expo` + `@testing-library/react-native` + `jest-native` für automatisierte Component-A11y-Assertions, plus `react-native-accessibility-engine` als Dev-Time-Linter, plus Maestro-Flows mit `id: "<accessibilityLabel>"`-Selektoren als E2E-Validation. Manuelle VoiceOver/TalkBack-Tour pre-Release. BIK BITV-Test als externer Audit pre-DiGA-Antrag.**
### 3.3 Setup-Aufwand
| Schritt | Aufwand |
| -------------------------------------------------------------- | --------- |
| `pnpm add -D jest-expo @testing-library/react-native @testing-library/jest-native` | 15 min |
| `jest.config.js` + `jest-setup.ts` mit jest-native-Matchern | 30 min |
| Erste 3 Component-A11y-Tests (Smoke) | 2 h |
| `react-native-accessibility-engine` integrieren | 1 h |
| Maestro-A11y-Selektoren in vorhandenen Flows umstellen | 1 h |
| Dokumentierte VoiceOver/TalkBack-Manual-Test-Checkliste | 2 h |
### 3.4 Beispiel — A11y-Component-Test
```typescript
// apps/rebreak-native/tests/components/AppHeader.a11y.test.tsx
import { render } from '@testing-library/react-native';
import '@testing-library/jest-native/extend-expect';
import { AppHeader } from '../../components/AppHeader';
describe('AppHeader — a11y contracts', () => {
it('Back-Button hat accessibilityLabel und role="button"', () => {
const { getByA11yLabel } = render(<AppHeader showBack title="Profil" />);
const back = getByA11yLabel('Zurück');
expect(back).toBeTruthy();
expect(back).toHaveAccessibilityRole('button');
});
it('Notification-Bell hat Label mit Badge-Count', () => {
const { getByA11yLabel } = render(<AppHeader notifCount={3} />);
expect(getByA11yLabel(/Benachrichtigungen.*3/i)).toBeTruthy();
});
it('Avatar/Dropdown-Trigger hat Label', () => {
const { getByA11yLabel } = render(<AppHeader />);
expect(getByA11yLabel(/Profilmenü/i)).toBeTruthy();
});
});
```
---
## 4. WCAG 2.1 Level AA — Mapping rebreak-native (heute)
Pflicht-Kriterien für DiGA, geprüft gegen Codebase 2026-05-07:
| WCAG-SC | Level | Status heute | Begründung |
| -------------------------------- | ----- | ------------ | ---------------------------------------------------- |
| 1.1.1 Non-text Content | A | 🔴 Fail | 97 % der Icon-Pressables ohne `accessibilityLabel` |
| 1.3.1 Info and Relationships | A | 🔴 Fail | Form-Labels in Demographics fehlen, Modal-Roles fehlen |
| 1.3.5 Identify Input Purpose | AA | ⚠️ Partial | Auth nutzt `autoComplete`, sonst nirgends |
| 1.4.3 Contrast (Minimum) Text | AA | 🔴 Fail | `#a3a3a3 / #ffffff` = 2,84:1 — siehe oben |
| 1.4.4 Resize Text | AA | ⚠️ Partial | RN font scales by default, aber Layout bricht bei XXL |
| 1.4.10 Reflow | AA | ⚠️ Unknown | nicht systematisch getestet |
| 1.4.11 Non-text Contrast | AA | 🔴 Fail | Switch-Border, Icon-Outlines auf vielen Hellgrau-Backgrounds |
| 2.1.1 Keyboard | A | n.a. | RN nativ (kein Keyboard-Use-Case auf Phone) |
| 2.2.2 Pause, Stop, Hide | A | 🔴 Fail | Animationen pausieren nicht bei Reduce-Motion |
| 2.4.3 Focus Order | A | 🔴 Unknown | Modals haben kein Focus-Trap → Order kaputt mit VoiceOver |
| 2.4.6 Headings and Labels | AA | 🔴 Fail | Headings wie ProfileHeader haben kein `accessibilityRole="header"` |
| 2.5.5 Target Size | AA | ⚠️ Partial | iOS 44pt via hitSlop OK, Android 48dp knapp |
| 3.2.1 On Focus | A | ✅ Pass | keine unerwarteten Context-Changes on Focus |
| 3.3.1 Error Identification | A | ⚠️ Partial | Errors als Text gerendert, aber kein `accessibilityLiveRegion="assertive"` |
| 3.3.2 Labels or Instructions | A | 🔴 Fail | Form-Inputs haben Placeholder statt Label |
| 4.1.2 Name, Role, Value | A | 🔴 Fail | überall fehlt Name/Role-Markup |
| 4.1.3 Status Messages | AA | 🔴 Fail | Toasts/SuccessAlert nicht als `accessibilityLiveRegion` |
**Zusammenfassung:** rebreak-native erfüllt heute ~3 von 17 für DiGA relevanten
WCAG-AA-Kriterien.
---
## 5. DiGA-Punkte-Strategy
### 5.1 Was BfArM real fragt (laut DiGA-Verfahrensverzeichnis)
DiGA-Antrag-Modul „Barrierefreiheit" verlangt:
- **Selbsterklärung WCAG 2.1 AA-Konformität** (Pflicht, schriftlich)
- **Test-Bericht** (BIK BITV-Test ODER eigener dokumentierter Audit)
- **Nutzergruppen-Reflexion** (welche Behinderungs-Pattern wurden wie adressiert?)
- **Process-Commitment** (wie wird A11y in Entwicklung+Releases sichergestellt?)
### 5.2 Low-hanging-fruit (hoher BfArM-Eindruck, niedriger Aufwand)
| Maßnahme | Effort | DiGA-Score |
| --------------------------------------------------------------------------- | ------ | ---------- |
| `accessibilityLabel` auf alle Icon-Pressables im SOS-Flow + Auth + Demographics | 1 Tag | hoch |
| `accessibilityRole="header"` auf alle h1/h2-Texte | 2 h | mittel |
| `useReduceMotion`-Hook + `Animated.timing` skip wenn true | 4 h | hoch |
| `accessibilityViewIsModal` auf alle 12 Modals | 3 h | mittel |
| Color-Tokens in `lib/theme.ts` auf WCAG-AA-konforme Hex anheben (`#a3a3a3``#737373`, etc.) | 4 h | hoch |
| Standard-Typing-Pattern für Forms: `<FieldRow accessibilityLabel={…}>` | 1 Tag | hoch |
### 5.3 Architektur-Investments (hoher Effort, höherer Score)
- **A11y-Wrapper-Komponenten**: `<A11yPressable>`, `<A11yTextInput>` zentral mit
Pflicht-Props. Migrationsweg über alle 453 Touchables.
- **Theme-Audit-Pipeline**: lint-rule die jede neue Hex-Color gegen
`getContrastRatio(fg, bg)` prüft.
- **CI-Gate**: jest-A11y-Tests und `react-native-accessibility-engine` in CI,
PR-Block bei Regression.
- **Dynamic-Type-aware-Layouts**: alle „fixed-width-Cards" auf flex-basiert
refactoren, Tests bei XXL-Font.
- **i18n-Pflicht-Audit**: jeder neue `accessibilityLabel` muss aus `t(…)`
kommen, nicht hartcodiert „Zurück" wie heute in AppHeader.
### 5.4 DiGA-Self-Statement (Vorschlag für DSFA mit Hans-Müller)
> rebreak verpflichtet sich zur Erfüllung der WCAG-2.1-Level-AA-Kriterien
> entsprechend BIK-BITV-Test-Standard. Pre-Release-Audit erfolgt durch
> [BIK-Provider], jährliches Re-Assessment ist Bestandteil unserer
> Entwicklungsprozesse. Automatisierte A11y-Component-Tests sind Bestandteil
> unseres CI-Gates (Pull-Request-Blocker bei Regression).
→ vor DiGA-Antrag prüfen mit Hans-Müller (DSB).
---
## 6. Roadmap
### 6.1 Pre-TestFlight (Wochenende 2026-05-09/10) — absolutes Minimum
Ziel: A11y-Apple-Review-Risk reduzieren, ohne den Cutover zu blockieren.
| Task | Form | Aufwand |
| --------------------------------------------------------------------------------- | ------------- | ------- |
| `accessibilityLabel` auf alle 7 Pressables in `app/urge.tsx` | manuell | 30 min |
| `accessibilityLabel` auf 4 OAuth + Forgot + Submit + Signup-Link in `signin.tsx` | manuell | 20 min |
| `accessibilityLabel` auf Notification-Bell + Avatar-Trigger in `AppHeader.tsx` | manuell | 10 min |
| `accessibilityLabel` auf Demographics-TextInputs + SelectButtons (8 Felder) | manuell | 30 min |
| `accessibilityViewIsModal={true}` auf SosFeedbackModal + GamePickerDrawer + InlineRatingDrawer + ProtectionDetailsSheet | manuell | 30 min |
| Manueller VoiceOver-Smoke-Walk (Login → SOS-Trigger → Lyra-Chip) auf iPhone-Build | manuell | 30 min |
**Owner:** rebreak-native-ui (UI-Edit-Approval beim User holen).
**Wichtig:** das ist NUR Pflaster für TestFlight Internal. Reicht nicht für DiGA.
### 6.2 Pre-DiGA-Antrag (Phase nach Public-Beta) — Vollabdeckung Critical Paths
| Task | Aufwand |
| ---------------------------------------------------------------------------------- | ------- |
| jest-expo + RNTL + jest-native installieren + jest.config.js | 1 h |
| `react-native-accessibility-engine` als Dev-Plugin | 1 h |
| A11y-Wrapper-Components `<A11yPressable>` + `<A11yTextInput>` | 1 Tag |
| Migration: alle 453 Pressables → A11yPressable mit Pflicht-Label | 5 Tage |
| Migration: alle 50 TextInputs → A11yTextInput mit Pflicht-Label | 1 Tag |
| `useReduceMotion()`-Hook in alle Animation-Files | 2 Tage |
| Color-Token-Audit in `lib/theme.ts` (WCAG-AA-konform) | 1 Tag |
| Headings-Roles auf alle Section-Titel | 4 h |
| `accessibilityLiveRegion="polite"` auf Toasts/SuccessAlert/CooldownBanner | 2 h |
| jest-A11y-Component-Tests (5 wichtigste Components, je 46 Assertions) | 1 Tag |
| Maestro-Flows: Selektoren auf accessibilityLabel umstellen | 1 Tag |
| Dokumentierte VoiceOver/TalkBack-Manual-QA-Checkliste | 0,5 Tag |
**Owner:** rebreak-native-ui + Ahmed (Tests).
### 6.3 Post-Launch — kontinuierliches A11y-Gate
| Task | Aufwand |
| ------------------------------------------------------------------------------- | ------- |
| GitHub Action: jest-A11y-Tests + RN-A11y-Engine in CI, PR-Block bei Regression | 2 h |
| BIK BITV-Test-Provider beauftragen pre-DiGA-Antrag | User-Action |
| Apple Accessibility Audit (Xcode) als Pre-Release-Step in `ops/` dokumentieren | 1 h |
| Android Accessibility Scanner als Pre-Release-Step | 1 h |
| jährliche A11y-Audit-Cycle (DSFA-Anhang) | User+Hans-Müller |
---
## 7. Konkrete TODOs nach Priorität
### Hoch (vor TestFlight, Wochenende)
1. SOS-Flow `app/urge.tsx` Lines 1094, 1116, 1123, 1160, 1209, 1231, 1244 — `accessibilityLabel` ergänzen (rebreak-native-ui).
2. Auth-Flow `(auth)/signin.tsx` Lines 103, 117, 139, 151, 162, 173, 186 — Labels (rebreak-native-ui).
3. AppHeader Lines 72, 88 — Notification + Avatar Labels (rebreak-native-ui).
4. Demographics Form Lines 267, 296, 304, 320, 328, 336, 351 — Labels (rebreak-native-ui).
5. Modals: `accessibilityViewIsModal` setzen (5 Sheets/Modals) (rebreak-native-ui).
### Mittel (Pre-DiGA-Antrag)
6. Test-Framework-Setup (`jest-expo`, RNTL, jest-native) (Ahmed).
7. A11y-Wrapper-Components (rebreak-native-ui + Ahmed-Konsultation für Test-Hooks).
8. Color-Token-Refactor in `lib/theme.ts` (rebreak-native-ui).
9. `useReduceMotion`-Cross-cutting (rebreak-native-ui).
10. BIK BITV-Test-Provider auswählen (User-Decision).
### Niedrig (Post-Launch)
11. CI-A11y-Gate (Ahmed + DevOps/Backyard).
12. Dokumentierte Pre-Release-Checkliste in `ops/RELEASE_READINESS.md` ergänzen (Ahmed).
13. Quartals-A11y-Re-Audit nach Feature-Release (DSFA-Anhang) (Ahmed + Hans-Müller).
---
## 8. Open Questions an User
1. **A11y-Bug-Fix-Scope am Wochenende vor TestFlight:** Soll rebreak-native-ui die ~25 fehlenden Labels in SOS-/Auth-/Demographics-/Header-Critical-Paths noch in den Cutover-Build einbauen (Effort ~2 h, Apple-Review-Risk-Reducer für External Beta), oder erst V2 nach Internal?
2. **BIK BITV-Test-Provider:** Soll Ahmed Provider-Vorschläge sammeln (BIT-inklusiv, BFIT-Bund, etc., Kosten 515k EUR), oder hat User schon Kontakt? Zeitpunkt: vor oder nach DiGA-Antrag-Submission?
3. **DiGA-Self-Statement-Wording:** Soll im DSFA-Anhang explizit „WCAG 2.1 AA"-Commitment stehen mit Test-Coverage-Quote (siehe TESTING_STATE.md §4.4) oder bewusst weicher formulieren („wir streben an…")? Hans-Müller-Frage.
---
Ende. — Ahmed