waffensachkunde
Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.
| 1 | /** |
| 2 | * Der Prüfmaßstab für Barrierefreiheit – an einer einzigen Stelle. |
| 3 | * |
| 4 | * ## Warum eine gemeinsame Konstante |
| 5 | * |
| 6 | * Der Regelsatz stand vorher an elf Stellen einzeln im Quelltext, und genau |
| 7 | * deshalb war er auseinandergelaufen: acht Stellen prüften gegen WCAG 2.1, |
| 8 | * zwei gegen 2.2, eine benutzte bereits eine Konstante. Der Anspruch dieses |
| 9 | * Projekts ist aber überall derselbe – **WCAG 2.2 AA**, siehe `PLAN.md` § 6.3. |
| 10 | * Eine Kopie, die man vergisst mitzuziehen, ist keine Nachlässigkeit im |
| 11 | * Kleinen: Sie senkt den Maßstab an einer Stelle, an der niemand hinsieht. |
| 12 | * |
| 13 | * ## Was der Regelsatz allein NICHT leistet |
| 14 | * |
| 15 | * Nachgemessen an axe-core 4.13.0: Am Tag `wcag22aa` hängt **genau eine** |
| 16 | * Regel – `target-size` (SC 2.5.8) –, und die steht auf `enabled: false`. |
| 17 | * Ein Tag in `runOnly` schaltet eine abgeschaltete Regel **nicht** ein. Wer |
| 18 | * die Tagliste erweitert und das als „wir prüfen jetzt 2.2 AA" verbucht, |
| 19 | * verbucht eine Null als Erfolg. |
| 20 | * |
| 21 | * Scharf wird die Regel erst über {@link FENSTER_REGELN}, und zwar |
| 22 | * ausschließlich im echten Fenster – siehe dort. |
| 23 | */ |
| 24 | export const WCAG_TAGS = ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'] as const; |
| 25 | |
| 26 | /** |
| 27 | * Regelabweichungen für Läufe in **jsdom** (Vitest, `app/tests`). |
| 28 | * |
| 29 | * ## Das ist kein Schlupfloch |
| 30 | * |
| 31 | * Beide Regeln gelten unverändert. Sie sind hier nur deshalb abgeschaltet, |
| 32 | * weil jsdom kein Layout rechnet und sie dort nichts messen **können** – sie |
| 33 | * würden nicht bestehen, sie würden schweigen oder, schlimmer, falsch |
| 34 | * bestehen. Geprüft werden sie im echten Fenster, siehe |
| 35 | * {@link FENSTER_REGELN}. |
| 36 | * |
| 37 | * - `color-contrast` braucht gerechnete Farben. Unter jsdom landet die Regel |
| 38 | * mangels CSS in `incomplete` statt in `violations` – ein stilles Nichts. |
| 39 | * - `target-size` ist noch heikler: Nachgemessen meldet sie unter jsdom für |
| 40 | * einen 8×8-Pixel-Knopf **„bestanden"**, weil jedes |
| 41 | * `getBoundingClientRect()` dort `0×0` liefert und die Nachbarschafts- |
| 42 | * rechnung degeneriert. Ein grünes Signal, das nichts gemessen hat, ist |
| 43 | * schlechter als gar keins. |
| 44 | */ |
| 45 | export const JSDOM_REGELN = Object.freeze({ |
| 46 | 'color-contrast': { enabled: false }, |
| 47 | 'target-size': { enabled: false }, |
| 48 | }); |
| 49 | |
| 50 | /** |
| 51 | * Regelabweichungen für Läufe im **echten Fenster** (Playwright, `app/e2e`). |
| 52 | * |
| 53 | * Hier rechnet Chromium Layout und Farben – erst hier lässt sich WCAG 2.2 AA |
| 54 | * tatsächlich messen. `target-size` muss ausdrücklich eingeschaltet werden, |
| 55 | * weil axe-core sie standardmäßig abgeschaltet ausliefert und der Tag sie |
| 56 | * nicht aktiviert. |
| 57 | */ |
| 58 | export const FENSTER_REGELN = Object.freeze({ |
| 59 | 'target-size': { enabled: true }, |
| 60 | }); |
| 61 | |
| 62 | /** Optionen für einen axe-Lauf unter jsdom. */ |
| 63 | export function jsdomOptionen(): { |
| 64 | runOnly: { type: 'tag'; values: string[] }; |
| 65 | rules: Record<string, { enabled: boolean }>; |
| 66 | } { |
| 67 | return { |
| 68 | runOnly: { type: 'tag', values: [...WCAG_TAGS] }, |
| 69 | rules: { ...JSDOM_REGELN }, |
| 70 | }; |
| 71 | } |
| 72 | |
| 73 | /** Optionen für einen axe-Lauf im echten Fenster. */ |
| 74 | export function fensterOptionen(): { |
| 75 | runOnly: { type: 'tag'; values: string[] }; |
| 76 | rules: Record<string, { enabled: boolean }>; |
| 77 | } { |
| 78 | return { |
| 79 | runOnly: { type: 'tag', values: [...WCAG_TAGS] }, |
| 80 | rules: { ...FENSTER_REGELN }, |
| 81 | }; |
| 82 | } |