- name
- ux-audit
- description
- Reproduzierbares UX-Audit von Online-Lösungen (Websites, Web-Apps, Dashboards) gegen einen versionierten Best-Practice-Katalog (Nielsen-Heuristiken, WCAG 2.2 AA, Core Web Vitals) — mit Live-Browser-Begehung UND Code-Analyse, gefolgt von Remediation-Plan, Re-Audit und GitHub-Release. Verwende diesen Skill wenn der User (1) ein UX-, Usability- oder Accessibility-Audit einer Website oder Web-App will, (2) fragt «ist meine App benutzerfreundlich / barrierefrei / WCAG-konform / schnell genug», (3) Verbesserungsvorschläge oder einen Umsetzungsplan für ein Frontend möchte, (4) Begriffe wie «UX-Audit», «Usability-Test», «Barrierefreiheit», «eCH-0059», «Heuristik-Evaluation», «Lighthouse», «Core Web Vitals» erwähnt, (5) nach einem Re-Audit oder Vorher-Nachher-Vergleich nach UX-Fixes fragt, (6) UX-Findings dokumentieren oder abarbeiten will, oder (7) allgemein sagt «schau dir mal die Seite an», «was würdest du am UI verbessern?», «ist das gut bedienbar?». Auch bei Redesign-Vorhaben und vor jedem Release einer Web-Lösung diesen Skill anwenden.
# UX Audit — Standardisiertes Audit-Vorgehen für Online-Lösungen
Dieser Skill kodiert ein reproduzierbares UX-Audit-Verfahren für Websites, Web-Apps und Dashboards gegen den versionierten Check-Katalog in `checks/` (~26 Checks in drei Kategorien: Usability, Accessibility, Performance). Ziel: dieselbe Methodik auf jede Online-Lösung im Portfolio anwenden, ohne dass der Auditor (oder Claude) die Kriterien jedes Mal neu erfindet — und den kompletten Lebenszyklus abdecken: **Audit → Plan → Umsetzung → Re-Audit → Release**.
**Das Mantra in drei Zeilen:**
1. **Begehen, nicht vermuten** — jeder Befund braucht Browser-Evidenz (Screenshot, Selektor, Messwert) oder Code-Stelle
2. **Profil zuerst, Checks danach** — applicability filtert alles
3. **Severity ohne Mitleid** — `critical` blockiert den Release, Punkt
## Lebenszyklus-Übersicht
```
Phase A — AUDIT Schritte 1-6: Profil → Katalog → Filter → Checks → Findings → Report+Plan
Phase B — REMEDIATION Findings nach Plan abarbeiten (ein Finding = ein Commit)
Phase C — RE-AUDIT Fehlgeschlagene Checks erneut prüfen, Delta-Report, Score-Vergleich
Phase D — RELEASE Dokumentation nach GitHub, CHANGELOG, Version-Bump, Release
```
Jede Phase ist einzeln aufrufbar («mach den Re-Audit» springt direkt zu Phase C, sofern `audit/` existiert). Abweichungen vom Vorgehen sind möglich, müssen aber im Audit-Report dokumentiert werden.
---
# Phase A — Audit
## Schritt 1: Profil laden oder erstellen
**Ziel:** Den Kontext der Lösung erfassen, damit nachfolgende Schritte die richtigen Checks filtern können. Das Profil lebt **dateibasiert im auditierten Repo** unter `audit/profile.yaml`. Existiert es nicht, wird es jetzt erstellt — Werte aus dem Repo/der URL ableiten wo möglich, den User nur für nicht ableitbare Felder fragen.
### 1.1 Pflichtfelder
| Feld | Werte | Verwendung im Audit |
|---|---|---|
| `name` | Kurzname der Lösung | Dateinamen, Report-Titel |
| `solution_url` | `https://…` / `none` | filtert Browser-Checks; `none` = nur Code-Analyse |
| `repo_path` | lokaler Pfad / `none` | filtert Code-Checks; `none` = nur Browser-Audit |
| `app_type` | `website` / `web-app` / `dashboard` | Gewichtung der Journeys |
| `target_audience` | `public` / `internal` / `expert` | Strenge bei Sprache & Hilfe |
| `languages` | Liste, z.B. `[de]` | Mehrsprachigkeits- und `lang`-Checks |
| `interaction` | `read-only` / `forms` / `transactional` | filtert Formular- & Fehlerpräventions-Checks |
| `compliance` | `none` / `eCH-0059` | öffentliche Hand ⇒ A11Y-Severity eine Stufe strenger |
| `viewports` | Subset von `[mobile, tablet, desktop]` | Responsive-Begehung |
| `baseline_report` | Pfad / `none` | `none` = Erst-Audit; sonst Re-Audit-Vergleichsbasis |
**Stopp-Regel:** Sind `solution_url` UND `repo_path` beide `none`, wird der Audit gestoppt — es gibt nichts zu prüfen. Fehlt nur eines, läuft der Audit im reduzierten Modus und der Report vermerkt das prominent («Code-Analyse nicht möglich, Repo nicht verfügbar»).
### 1.2 Verzeichnisstruktur im Ziel-Repo
Der Audit legt (falls nicht vorhanden) diese Struktur an:
```
<ziel-repo>/audit/
├── profile.yaml # Schritt 1
├── findings/ # Schritt 5 — eine Datei pro Finding
│ └── 2026-07-13-A11Y-005.md
├── screenshots/ # Browser-Evidenz
├── remediation-plan.md # Schritt 6
└── reports/
├── 2026-07-13-audit.md
└── 2026-07-27-re-audit.md
```
Alles ist versioniert — der Re-Audit-Vergleich läuft über diese Dateien und die Git-Historie, kein externer Tracker nötig.
---
## Schritt 2: Check-Katalog laden
**Ziel:** Den vollständigen Katalog (`checks/*.md`) parsen und nach `category` + `severity` indizieren. `checks/MANIFEST.txt` listet alle Checks mit einer Zeile Metadaten — zuerst lesen, dann nur die anwendbaren Check-Dateien vollständig laden.
### 2.1 Drei Kategorien
| Kategorie | Quelle | Checks |
|---|---|---|
| `USE` | Nielsen-Heuristiken, adaptiert für Web-Lösungen | 10 |
| `A11Y` | WCAG 2.2 AA, eCH-0059 | 10 |
| `PERF` | Core Web Vitals, wahrgenommene Performance | 6 |
### 2.2 Severity-Stufen
| Stufe | Bedeutung | Konsequenz |
|---|---|---|
| `critical` | Nutzergruppe komplett ausgeschlossen oder Kernaufgabe unlösbar | Blockiert Release. Muss vor Go-Live gefixt sein. |
| `high` | Signifikante Hürde für viele Nutzer oder Compliance-Risiko | Im laufenden Sprint fixen. |
| `medium` | Best-Practice-Verletzung, spürbare Reibung | Im nächsten Sprint planen. |
| `low` | Polish, Feinschliff | Backlog. |
**eCH-0059-Eskalation (differenziert nach WCAG-Level).** eCH-0059 verpflichtet die öffentliche Hand auf **WCAG 2.1 Level AA**. Deshalb wird bei `compliance: eCH-0059` ein `A11Y`-Check **eine Stufe strenger** bewertet (medium→high, high→critical) — **aber nur, wenn der verletzte Aspekt auf Level A oder AA liegt** (Feld `wcag_level` im Check). Ein Check, dessen Anforderung nur aus Level AAA stammt, wird **nicht** eskaliert — AAA ist über die gesetzliche Pflicht hinaus wünschenswert, aber kein Konformitätskriterium.
Checks mit gemischtem Level (`wcag_level: A/AAA`, z.B. A11Y-009) eskalieren nur, wenn der **tatsächlich beobachtete** Verstoss den A/AA-Anteil trifft. Der Check dokumentiert in seiner `eCH-0059-Eskalation`-Sektion, wie zu differenzieren ist. Das verhindert, dass eine pauschale Regel Findings härter blockiert, als die Rechtslage es verlangt.
### 2.3 Check-Schema
Jeder Check ist eine eigenständige Markdown-Datei:
```markdown
---
id: A11Y-005
title: "Textkontrast mindestens 4.5:1"
category: A11Y
severity: high
applies_when: 'always'
wcag_ref: "1.4.3"
wcag_level: AA # nur A11Y: steuert die eCH-0059-Eskalation (A/AA eskaliert, AAA nicht)
evidence_required: 2
---
# Body mit Description, Verification (pro Modus), Pass Criteria, Remediation
```
---
## Schritt 3: Applicability-Filter
**Ziel:** Aus dem Katalog nur die Checks auswählen, die für das Profil relevant sind. Ohne Filter überfluten irrelevante Findings den Report (z.B. Formular-Checks für eine read-only-Informationsseite).
### 3.1 `applies_when`-Auswertung
Boolean-Ausdruck gegen die Profil-Felder, gleiche Syntax wie bei mcp-audit: `==`, `!=`, `.includes(...)`, `and` / `or`, `always`. Beispiele:
| Klausel | Effekt |
|---|---|
| `always` | Check läuft immer |
| `interaction != "read-only"` | nur bei Formularen/Transaktionen |
| `solution_url != "none"` | nur wenn Live-Begehung möglich |
| `languages.includes("de") and languages.includes("fr")` | nur bei Mehrsprachigkeit |
### 3.2 Applicability-Report (vor Audit-Start)
Bevor der eigentliche Audit beginnt, diese Übersicht ausgeben:
```
=== Audit applicability for zuerich-schulen-dashboard ===
Profile: web-app, public audience, de, forms, eCH-0059,
URL: https://…, Repo: lokal verfügbar
Applicable checks: 24 / 26
USE: 9/10 (USE-007 Shortcuts: nur für expert audience)
A11Y: 10/10 (eCH-0059 ⇒ Severity-Eskalation aktiv)
PERF: 5/6 (PERF-005 Header: kein Repo-Zugriff auf Server-Config)
Severity breakdown: critical: 3 high: 12 medium: 7 low: 2
```
Nicht anwendbare Checks erscheinen **gar nicht** im Report — nicht einmal als «N/A».
---
## Schritt 4: Check-Ausführung
**Ziel:** Jeden anwendbaren Check methodisch verifizieren. Das Hybrid-Modell kombiniert vier Verifikationsmodi:
| Modus | Wann | Werkzeug |
|---|---|---|
| `browser_test` | Verhalten, Rendering, Interaktion | In-App-Browser: Accessibility-Tree (`read_page`), JS-Messung (`javascript_tool`: `getComputedStyle`, `getBoundingClientRect`), Konsole, Netzwerk, `resize_window` für Viewports; Screenshot wo möglich |
| `code_review` | Semantik, Struktur, Patterns | Read/Grep im Repo |
| `automated` | Pattern existiert/fehlt | grep nach `alt=`, `aria-`, `lang=`, `prefers-reduced-motion` … |
| `tooling` | Messwerte **und** automatisierte A11Y-Prüfung | `npx lighthouse <url> --only-categories=performance,accessibility --output=json` — läuft in echtem Headless-Chrome, **unabhängig vom Audit-Browser**. Liefert nicht nur CWV, sondern auch `color-contrast`, `button-name`, `heading-order`, `link-name`, `image-alt`. Falls nicht verfügbar: Netzwerk-Panel + manuelle Messung, im Report als «approximiert» kennzeichnen |
**Lighthouse ist bei client-gerenderten Apps kein Nice-to-have.** Bei `app_type: web-app` / `dashboard` früh laufen lassen, nicht erst für PERF: Es rendert die Seite in einem echten Browser und liefert damit oft die einzige belastbare Evidenz, wenn der Audit-Browser die interaktive Ansicht nicht zum Laufen bringt (siehe 4.1.1). Sein A11Y-Teil deckt Kontrast, zugängliche Namen und Heading-Order automatisiert ab — genau die Checks, die sonst manuelle Interaktion bräuchten.
### 4.1 Browser-Begehungsprotokoll
Die Live-Begehung folgt einem festen Protokoll, damit Evidenz reproduzierbar ist:
1. **Kern-Journeys definieren** (2–4 Stück, aus `app_type` ableiten oder User fragen): z.B. «Startseite → Suche → Detailansicht», «Formular ausfüllen → absenden → Bestätigung»
2. **Pro Journey und Viewport durchklicken** — an jedem Schritt: `read_page` (Accessibility-Tree), Konsole auf Fehler prüfen, Screenshot wo möglich
3. **Tastatur-Durchgang** — dieselbe Journey nur mit `Tab`/`Enter`/`Escape` (Basis für A11Y-001/002)
4. **Messungen** — Netzwerk-Panel für Asset-Gewichte, Lighthouse für CWV, `javascript_tool` für Kontrast/Zielgrössen/Layout
**Evidenz-Fallback (wichtig):** Das Screenshot-Werkzeug im Browser-Pane ist nicht immer stabil (Timeouts bei schweren Seiten). Screenshots sind **Best-Effort, keine Pflicht**. Gleichwertige — oft präzisere — Evidenz liefern der **Accessibility-Tree** (`read_page`) und **JS-Messungen** (`javascript_tool`): Kontrast via `getComputedStyle` + WCAG-Formel, Zielgrössen via `getBoundingClientRect`, Fokus-Stil via `document.activeElement`, Reflow via `scrollWidth`-Vergleich. Diese sind reproduzierbar und zitierfähig (Selektor + Messwert). Wenn Screenshots scheitern, im Report unter «Abweichungen» vermerken und mit Tree/JS-Evidenz weiterarbeiten — der Audit ist deswegen **nicht** unvollständig.
Screenshots (falls erzeugt) mit sprechendem Namen in `audit/screenshots/` ablegen (`a11y-002-fokus-unsichtbar-suche.png`).
### 4.1.1 Hydration-Probe: erst prüfen, ob die App überhaupt lebt
**Vor** dem ersten Interaktions-Check klären, ob die Seite im Audit-Browser tatsächlich hydriert ist. Sonst besteht die grösste Gefahr dieses Skills: **ein Umgebungslimit als Produktfehler zu melden** — «der Tooltip öffnet nicht» zu schreiben, obwohl der Tooltip tadellos ist und nur der Audit-Browser die App nicht mountet.
```javascript
// 1. Meldet sich der Tab überhaupt als sichtbar?
document.visibilityState // "hidden" -> rAF-getriebene Views (Canvas, Cytoscape,
// D3, WebGL, Karten) mounten evtl. nie
// 2. Hydriert React/Vue? Ein simples Client-Control klicken und Wirkung prüfen:
themeToggle.click(); document.documentElement.className // ändert sich -> hydriert
// 3. Hängt die Seite in einem Route-Level-Loading/Suspense-Fallback?
document.body.textContent.includes('Lade …') // + erwartete Container fehlen
```
**Interpretation:**
| Beobachtung | Bedeutung | Konsequenz |
|---|---|---|
| Ein simples Control reagiert, die Zielansicht aber nicht | Nur **dieses Segment** hydriert nicht (Suspense/Gating) | Interaktions-Checks dort → `unverified`, nicht `fail` |
| Kein einziges Control reagiert | Die ganze Seite hydriert nicht | Interaktions-Checks → `unverified`; ggf. echter App-Bug — nur dann Finding, wenn auch Lighthouse/echter Browser es zeigt |
| Alles reagiert | Normalbetrieb | Begehung wie gewohnt |
**Eskalationsleiter, wenn die interaktive Ansicht nicht mountet** (in dieser Reihenfolge, nicht überspringen):
1. **DOM/CSS-Evidenz** — viele Checks brauchen gar keine Interaktion: gelieferte CSS-Regeln (`document.styleSheets`), Heading-Ebenen, ARIA-Attribute, `lang`, Alt-Texte. Struktur-Fixes sind hier **vollwertig** verifizierbar.
2. **Lighthouse** — echtes Headless-Chrome, rendert unabhängig vom Audit-Browser; deckt Kontrast, zugängliche Namen, Heading-Order, CWV ab.
3. **Build/Typecheck** (falls Repo vorhanden) — belegt Kompilierbarkeit und, bei SSG, dass jede Route/Sprache serverseitig rendert.
4. **Hand-off an den Menschen** — was danach übrig bleibt, wird als `unverified` markiert und im Report §Vorbehalte konkret zur manuellen Prüfung übergeben («`ⓘ` per Tab fokussieren → Enter öffnet, Escape schliesst»).
**Eselsbrücke:** *«Wenn der Patient nicht atmet, ist nicht das Stethoskop kaputt — aber prüf erst, ob du das Stethoskop richtig hältst.»*
### 4.2 Reihenfolge: Severity descending
`critical` zuerst (Showstopper früh erkennen), dann `high`, `medium`, `low`. **Wenn ein `critical`-Check fehlschlägt, kann der Audit nicht «pass» erhalten** — egal wie gut der Rest ausfällt.
### 4.3 Evidenz-Sammlung pro Check
```yaml
check_run:
id: A11Y-002
status: pass | fail | partial | unverified | skip
evidence_collected: 3
evidence_required: 2
findings:
- "Fokus-Indikator fehlt auf Such-Button — Screenshot a11y-002-fokus-unsichtbar-suche.png"
- "CSS: outline: none in src/styles/global.css:112 ohne Ersatz"
gaps:
- "Nur Desktop geprüft, Mobile-Tastatur (externe Tastatur am Tablet) offen"
evaluator_notes: |
Der Fokus-Stil wurde global entfernt, vermutlich aus ästhetischen
Gründen. Fix ist trivial, Impact hoch.
```
Browser-Evidenz referenziert **URL + Selektor + (Screenshot oder Messwert)**, Code-Evidenz **Datei + Zeile**. Ein gemessener Wert (`Kontrast 2.8:1 an button.search-submit`) ist gleichwertig zu einem Screenshot — oft sogar stärker, weil objektiv. Ein Finding ohne jede Verankerung ist eine Meinung, kein Befund.
### 4.4 Pass-Criteria
Ein Check besteht **nur dann** als `pass`, wenn alle Pflicht-Pass-Criteria erfüllt sind, mindestens `evidence_required` Beobachtungen vorliegen und keine `gaps` der Severity ≥ Check-Severity offen sind. Sonst: `partial` (50%+ erfüllt) oder `fail`.
### 4.5 Der Status `unverified`
`unverified` heisst: **Die Prüfung konnte nicht durchgeführt werden — nicht, dass sie fehlgeschlagen ist.** Reserviert für Umgebungslimits (Zielansicht hydriert nicht, Werkzeug nicht verfügbar, Zugang fehlt), nachdem die Eskalationsleiter aus 4.1.1 abgearbeitet ist.
Regeln, die diesen Status vor Missbrauch schützen:
- **Nie als Bequemlichkeits-Ausweg.** Erst wenn alle vier Stufen der Leiter erschöpft sind. «Ich habs nicht hingekriegt» ist kein Umgebungslimit.
- **Immer mit Ursache + Hand-off.** Der Report nennt den technischen Grund (`visibilityState: hidden` → Graph mountet nicht) **und** die konkrete manuelle Prüfanweisung.
- **Teilverifikation ausweisen.** Meist ist ein Teil belegt: «Struktur/ARIA bestätigt (Lighthouse `button-name: 1`), Öffnen-Interaktion offen». Das gehört ins Finding, nicht in den Papierkorb.
- **Zählt nicht in den Score** (siehe 6.3) — weder als pass noch als fail. Ein unverifizierter Check darf den Score weder schönen noch strafen.
- **Erscheint prominent im Report**, nie stillschweigend. Ein verschwiegener `unverified`-Check ist schlimmer als ein `fail`: er täuscht Abdeckung vor.
**Ein `unverified` bei einem `critical`-Check blockiert «release-ready»** genauso wie ein offenes `fail`. Bei Barrierefreiheit gilt: nicht nachgewiesen = nicht erfüllt.
---
## Schritt 5: Finding-Dokumentation
**Ziel:** Pro fehlgeschlagenem Check (`fail`/`partial`) ein strukturiertes Finding nach `templates/finding.md` erzeugen, abgelegt als `audit/findings/<datum>-<check-id>.md`.
Kern-Felder: Severity, Status (`open`/`in-remediation`/`accepted-risk`/`closed`), Observed/Expected Behavior, Evidenz (Screenshot + Selektor und/oder Datei:Zeile), Risiko konkret («Screenreader-Nutzer können die Suche nicht bedienen» — nicht «Accessibility suboptimal»), Remediation mit Code-Diff, Effort S/M/L/XL, Verification-After-Fix.
View on GitHub