---
name: lighthouse
description: |
  Iteriert den Lighthouse-Score einer Live-URL Richtung 100/100/100/100 — ohne das
  Design anzufassen. Triggert bei `/lighthouse <url>`, „lighthouse score auf 100",
  „performance optimieren", „seite optimieren" oder vergleichbar.
  Identifiziert das passende Repo, arbeitet auf Feature-Branches off main, misst via
  PageSpeed Insights API, fixt ausschließlich design-neutrale Audits, deployt und loopt
  bis 100 oder bis kein weiterer Fix möglich ist, ohne das Design zu verändern.
---

# /lighthouse — Performance auf 100 iterieren

> **Standardisierte Version.** Das ist der Skill, mit dem wir bei CEELIS unsere
> Kunden-Seiten auf 100/100/100/100 bringen — von firmenspezifischen Dingen (unsere
> Repos, unser Hosting, unsere Secrets) befreit. Überall, wo du etwas an dein eigenes
> Setup anpassen musst, steht ein `{{PLATZHALTER}}`. Wie du den Skill installierst und
> auf dich anpasst, steht ganz unten unter **Installation & Anpassung**.
>
> Gebaut für [Claude Code](https://claude.com/claude-code). Drop-in: `~/.claude/skills/lighthouse/SKILL.md`.

## Ziel

Du tippst `/lighthouse https://deine-seite.de`. Der Skill bringt **Performance /
Accessibility / Best Practices / SEO** schrittweise auf 100 — **ohne Design-Änderungen**.

## Hard-Rule #1: Design bleibt wie es ist

Optimierungen ausschließlich auf Render-/Network-/Markup-Ebene. Layout, Farben,
Typografie, Komponenten-Struktur, Abstände, Animationen sind **tabu**. Ein
Lighthouse-Lauf darf die Seite messbar schneller machen, aber niemals anders aussehen
lassen.

### Erlaubt (Whitelist)
- `loading="lazy"` + `decoding="async"` auf Below-Fold-Bildern
- Preload/Preconnect-Hints für Above-Fold-Assets
- `defer` / `async` / `media="print" onload=…`-Swap auf nicht-kritischem CSS/JS
- Image-Konvertierung PNG/JPG → WebP/AVIF **bei identischer Pixel-Größe**
- `srcset` + `sizes` ergänzen ohne Aspect-Ratio zu ändern
- `width` + `height` Attribute auf `<img>`/`<video>` (verhindert CLS)
- Font-Subsetting, `font-display: swap`
- Cache-Header / `Content-Encoding: br|gzip` (via Host-Config oder `_headers`)
- Unused-CSS/JS Removal **nur wenn echter Dead Code** — keine genutzten Selektoren killen
- Critical-CSS-Inlining (viele Build-Tools machen das schon, z. B. Astros `inlineStylesheets`)
- Meta-Tags ergänzen (canonical, description, lang, theme-color, viewport)
- Alt-Texte nachpflegen
- ARIA-Labels für Icon-Buttons, sprechende Link-Texte statt „hier klicken"
- Sitemap / robots.txt Fixes
- `<html lang="…">` setzen falls noch nicht da
- Heading-Hierarchie korrigieren **nur** wenn semantisch falsch UND visuell identisch

### Verboten (Blacklist) — NIE anwenden
- CSS-Layout-Properties (grid, flex, padding, margin, width, height, gap)
- Farben, Schatten, Border-Radius, Background-Images
- Fonts oder Font-Sizes ersetzen
- Komponenten löschen, verstecken, anders ordnen
- Bilder croppen / Aspect-Ratio ändern / visuell ersetzen
- Section-Reihenfolge umstellen
- Animationen entfernen, die zum Design gehören (deine Fade-/Reveal-Klassen etc.)
- Drittquellen-Embeds (Maps, Videos, Social-Embeds) ohne Rückfrage entfernen
- Analytics- / Tracking-Scripts ohne Rückfrage entfernen
- `tap-targets` / Touch-Target-Größe vergrößern (verändert Layout)

Im Zweifel: NICHT anwenden. In den Iterations-Output schreiben „skip — Design-Impact".

## Hard-Rule #2: Immer Feature-Branch, nie direkt auf main

Alle Code-Änderungen passieren auf einem Feature-Branch off `main`. Direkt auf main
schreiben ist verboten. Squash-Merge + Branch-Delete pro Iteration.

## Ablauf

### Step 0 — Argumente parsen

Input: die zu optimierende URL.

Falls leer: „Welche URL soll ich optimieren?" fragen und stoppen.

```
URL  = $ARGUMENTS (getrimmt, falls ohne https:// → ergänzen)
HOST = URL ohne Protokoll/Path
```

### Step 1 — URL → Repo identifizieren

Heuristiken in dieser Reihenfolge:

1. **Bist du schon im richtigen Repo?** `git remote get-url origin` — enthält die Remote
   einen Namen, der zum HOST passt? Dann weiter zu Step 2.
2. **Lokal vorhanden?** Prüfe deine üblichen Projekt-Ordner (`{{REPO_LOCATIONS}}`, z. B.
   `~/Projects/<name>`, `~/code/<name>`) auf einen passenden Ordner.
3. **Sonst clonen:**
   ```bash
   gh repo clone {{GITHUB_ORG}}/<repo-name> ~/Projects/<repo-name>
   cd ~/Projects/<repo-name>
   npm install   # Dependencies — sonst kein Build möglich
   ```

Falls nichts greift: fragen „Welches Repo gehört zu `<HOST>`?" und stoppen.

### Step 2 — Working Dir + Repo-State checken

```bash
REMOTE=$(git remote get-url origin 2>/dev/null)
git fetch origin main
```

| Branch | Clean | Up-to-date | Aktion |
|---|---|---|---|
| main | ja | ja | weiter zu Step 3 |
| main | ja | nein | `git pull --ff-only` |
| main | nein | * | fragen: stash OK? |
| feature | ja | * | `git checkout main && git pull` |
| feature | nein | * | fragen: stash + auf main? |

### Step 3 — Baseline-Lighthouse-Run

Via PageSpeed Insights API. **API-Key ist Pflicht** — ohne `key=…` antwortet Google mit
HTTP 429 (`quota_limit_value: 0`). Wie du an einen Key kommst, steht unter
**Installation & Anpassung → PageSpeed-API-Key**. Der Skill liest ihn aus der Env-Var
`PAGESPEED_API_KEY`.

```bash
mkdir -p /tmp/lh-$(date +%Y%m%d-%H%M%S)
LH_DIR=$(ls -1dt /tmp/lh-* | head -1)
KEY="${PAGESPEED_API_KEY}"
[ -z "$KEY" ] && { echo "PAGESPEED_API_KEY nicht gesetzt — siehe Installation & Anpassung"; exit 1; }

# Mobile (Google priorisiert mobile-first)
curl -sS "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${URL}&strategy=mobile&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&key=${KEY}" \
  > "${LH_DIR}/baseline-mobile.json"

# Desktop
curl -sS "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${URL}&strategy=desktop&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&key=${KEY}" \
  > "${LH_DIR}/baseline-desktop.json"
```

**Mobile-PSI ist oft bimodal volatil** auf Edge-/CDN-gehosteten URLs (Cache-Warmup): zwei
Score-Cluster pro Seite sind normal. Bei Final-/Vergleichsmessungen 2–3 Runs nehmen und
den Median berichten, nicht einen Single-Run.

Scores parsen via `jq`:
```bash
jq '.lighthouseResult.categories | to_entries | map({(.key): (.value.score*100|round)}) | add' baseline-mobile.json
# z. B. {"performance":78, "accessibility":92, "best-practices":83, "seo":100}
```

Issues sammeln (alle Audits mit `score < 1`):
```bash
jq '.lighthouseResult.audits | to_entries | map(select(.value.score != null and .value.score < 1)) | map({id: .key, title: .value.title, score: .value.score, savings: .value.displayValue})' baseline-mobile.json
```

**Fallback bei API-Fehler:** lokal `npx lighthouse <URL> --output=json --output-path=$LH_DIR/baseline.json --quiet --chrome-flags="--headless --no-sandbox"` — braucht headless Chrome lokal.

### Step 4 — Issues priorisieren

1. Niedrigste Score-Kategorie zuerst
2. Whitelist-Match (Hard-Rule #1)
3. Highest-Impact-Audit zuerst (LCP / CLS / FCP-Savings absteigend)
4. Aufwand: einfach > komplex (Single-File-Edit > Multi-File-Refactor)

**Audit → Fix Mapping (häufige Fälle):**

| Audit-ID | Fix |
|---|---|
| `uses-webp-images` / `modern-image-formats` | jpg/png zu WebP/AVIF konvertieren (`cwebp`/`avifenc`), Refs in HTML/CSS updaten |
| `unsized-images` | `width`+`height` Attribute auf `<img>` |
| `offscreen-images` | `loading="lazy"` + `decoding="async"` |
| `render-blocking-resources` | nicht-kritisches CSS mit `media="print"` + `onload` swap; JS mit `defer` |
| `unminified-javascript` / `unminified-css` | Build-Config checken (die meisten Bundler minifyen by default — wahrscheinlich 3rd-party) |
| `unused-css-rules` | Purge-Config deines CSS-Frameworks enger ziehen — **nur** Dead-Selektoren |
| `font-display` | `&display=swap` an Webfont-URL anhängen |
| `uses-text-compression` | `_headers`-File mit `Content-Encoding` oder Host-Config |
| `uses-long-cache-ttl` | Cache-Header für gehashte Build-Assets (1 Jahr, immutable) |
| `meta-description` | Description-Meta im Layout setzen |
| `document-title` | `<title>` setzen falls fehlt |
| `html-has-lang` | `<html lang="…">` |
| `image-alt` | `alt` nachpflegen (leerer String `alt=""` für rein dekorative Bilder) |
| `button-name` / `link-name` | `aria-label` oder sprechender Text |
| `color-contrast` | **PRÜFEN** — Farb-Änderung ist Design-Impact. Bei klarer a11y-Verletzung vorher fragen. |
| `tap-targets` | **SKIP** — Layout-Impact, nie automatisch |
| `viewport` | Meta-Viewport ergänzen falls fehlt |
| `is-crawlable` | robots.txt / meta robots prüfen |
| `legacy-javascript` | Modernes Build-Target setzen (Bundler-Config) |

### Step 5 — Iteration ausführen

Pro Audit ein Feature-Branch + ein PR:

```bash
ITER=1
git checkout -b perf/lh-iter-${ITER}-<audit-slug>
```

1. Code-Änderung anwenden
2. `{{BUILD_CMD}}` (z. B. `npm run build`) lokal — failed? → fixen oder revert
3. **Visuell verifizieren** (best effort): Preview starten, HTML der Hauptseite gegen den
   Vor-Iter-Snapshot vergleichen — Body-Markup-Diff sollte minimal sein. Bei
   Image-Konvertierung: Pixel-Dimensionen Output == Input.
4. `git add <files>` (kein `git add -A` — explizit)
5. `git commit -m "perf(<scope>): <audit-id> fix"`
6. `git push -u origin perf/lh-iter-${ITER}-<audit-slug>`
7. `gh pr create` mit Test-Plan
8. `gh pr merge --squash --delete-branch`

### Step 6 — Re-Deploy abwarten

Die meisten Hosts (Cloudflare Pages, Vercel, Netlify) deployen automatisch beim
`main`-Merge. Wait-Loop (max ~5 Min):

```bash
sleep 90
# Hash-Compare: gehashte Build-Assets ändern sich bei neuem Build
NEW_HASH=$(curl -s "$URL" | grep -oE '{{ASSET_HASH_PATTERN}}' | head -1)   # z. B. _astro/[^"]+\.css
# mit Pre-Deploy-Hash vergleichen — falls gleich, weitere 30s, max 3 Retries
```

Wenn dein Host **nicht** auto-deployt: hier deinen Deploy-Befehl (`{{DEPLOY_CMD}}`)
einsetzen.

### Step 7 — Re-Measure + Compare

PageSpeed-API erneut. Ergebnis-Tabelle:

| Kategorie | Baseline | Iter N | Δ |
|---|---|---|---|
| Performance | 78 | 84 | +6 |
| Accessibility | 92 | 100 | +8 |
| Best Practices | 83 | 100 | +17 |
| SEO | 100 | 100 | 0 |

### Step 8 — Loop oder Stop

**Stop-Bedingungen (any):**
- Alle 4 Kategorien = 100 → done, „🎯 100/100/100/100 erreicht"
- Score-Regression nach Fix → revert, Audit als „risky" markieren, skip
- Score stagniert über 2 Iterationen → done („Plateau erreicht")
- Nur noch Issues mit Design-Impact übrig → done („alles Design-neutrale gefixt")
- Max **8 Iterationen** pro Session (Token-/Zeit-Schutz)

Sonst → zurück zu Step 4 mit neuem Issues-Set.

### Step 9 — Final-Output

```
# Lighthouse-Iteration für <URL>

## Ergebnis
| Kategorie | Vorher | Nachher |
|---|---|---|
| Performance | 78 | 100 |
| Accessibility | 92 | 100 |
| Best Practices | 83 | 100 |
| SEO | 100 | 100 |

## Iterationen
1. PR #X — `meta-description` + `html-has-lang`
2. PR #Y — `uses-webp-images` (5 Bilder konvertiert)
…

## Offen (Design-Impact, nicht angefasst)
- `tap-targets`: 3 Links unter 48×48px — würde Padding-Änderung erfordern
- `color-contrast`: Footer-Subtext zu blass — würde Brand-Farbe ändern. Entscheidung nötig.
```

## Was du NIE machst

- **Force-Push auf main** (auch wenn alles grün)
- **Bilder croppen** oder Aspect-Ratio ändern
- **Fonts ersetzen** (System-Font wäre ein Performance-Win, aber Design-Impact)
- **Analytics / Tracking entfernen** ohne explizite Rückfrage
- **Mehr als 8 Iterationen** pro Session — sonst neue Session
- **Audits fixen, die nicht in der Whitelist stehen** — im Zweifel skippen, auch wenn der
  Score-Gewinn groß wäre
- **Dependencies bumpen** als Lighthouse-Fix — separater Concern
- **`.env` / Secrets committen**

## Edge-Cases

- **URL nicht erreichbar:** vor Step 3 `curl -sI -o /dev/null -w "%{http_code}" $URL` — bei ≠ 2xx melden und stoppen.
- **Repo hat kein Build-Script:** stoppen mit „Repo hat kein `build`-Script — kann nicht deployen".
- **Kein Auto-Deploy:** wenn nach 5 Min kein neuer Build live ist → melden statt blind weiterloopen.
- **Uncommitted Changes:** Stash-Vorschlag mit Bestätigung; nach Skill-Ende kein Auto-Pop.
- **Score-Regression nach Fix:** revert-Commit, Audit als „kontraindiziert" markieren, weiter.

## Installation & Anpassung

### Installieren (Claude Code)
1. Lege die Datei unter `~/.claude/skills/lighthouse/SKILL.md` ab (Ordnername = Skill-Name).
2. Starte Claude Code neu bzw. lass die Skills neu einlesen.
3. Tippe `/lighthouse https://deine-seite.de`.

### Auf dein Setup anpassen
Ersetze die Platzhalter (einmalig):

| Platzhalter | Was rein muss |
|---|---|
| `{{GITHUB_ORG}}` | Deine GitHub-Org / dein User für `gh repo clone` |
| `{{REPO_LOCATIONS}}` | Wo deine Repos lokal liegen (z. B. `~/Projects`) |
| `{{BUILD_CMD}}` | Dein Build-Befehl (`npm run build`, `pnpm build`, `astro build`, …) |
| `{{DEPLOY_CMD}}` | Nur falls dein Host **nicht** auto-deployt — sonst leer lassen |
| `{{ASSET_HASH_PATTERN}}` | Das Muster deiner gehashten Build-Assets fürs Deploy-Polling (Astro: `_astro/[^"]+\.css`, Vite: `assets/[^"]+\.js`) |

### PageSpeed-API-Key
1. [Google Cloud Console](https://console.cloud.google.com/) → Projekt anlegen/wählen.
2. **PageSpeed Insights API** aktivieren (APIs & Services → Library).
3. Unter „Credentials" einen **API-Key** erstellen.
4. Key als Env-Var setzen, damit der Skill ihn findet:
   ```bash
   # macOS/Linux (in ~/.zshrc oder ~/.bashrc):
   export PAGESPEED_API_KEY="dein-key"
   # Windows PowerShell:
   setx PAGESPEED_API_KEY "dein-key"
   ```
   Der Key ist kostenlos und für dieses Volumen weit unter dem Free-Tier-Limit. Nicht ins
   Repo committen.

### Optional
- Wenn du wiederkehrende Lessons festhalten willst (z. B. „cwebp ist installiert"), häng
  dir ans Ende eine kurze Notiz-Routine — der Standard-Skill macht das bewusst nicht.

---

Gebaut von **CEELIS** · [ceelis.com](https://ceelis.com). Der Rest unseres Entwicklungs-Stacks
und weitere Skills laufen in unserer Community → [ceelis.com/ki-community](https://ceelis.com/ki-community).
