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>
433 lines
27 KiB
Markdown
433 lines
27 KiB
Markdown
# 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) | 108–239 | 🔴 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 4–6 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 5–15k 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
|