Formular-Regeln in CLAUDE.md; A11y-Fixes: Feldränder 3:1-Kontrast (neuer Token), Checkbox 24px + größeres Label, aria-live-Region nie display:none

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 21:59:34 +02:00
parent ee0d6866ea
commit b95a0bce2d
3 changed files with 49 additions and 26 deletions

View File

@@ -38,6 +38,7 @@ selbst gehosteter Stack ohne Abhängigkeiten von Drittanbietern.
| Startseiten-Inhalte | `data/home.json` | Texte in `pages/home.php` |
| Partner/Sponsoren | `data/partners.json` | — |
| Instagram-Cache | `data/instagram.json` + `public/assets/img/instagram/` | **maschinenverwaltet von `bin/instagram-sync.php` — nie von Hand editieren** |
| Icons | `public/assets/icons/` (Bootstrap Icons, lokal) via `icon()`-Helper | Inline-SVG-Pfade von Hand ins Markup, Emoji/Unicode-Glyphen, gezeichnete CSS-Icons, Icon-Font/CDN |
Per-Page-Meta (Title/Description/OG) lebt in der jeweiligen Page-Datei (`app/pages/*.php`) im `$meta`-Array.
@@ -50,7 +51,8 @@ Per-Page-Meta (Title/Description/OG) lebt in der jeweiligen Page-Datei (`app/pag
prüfen ob eine Komponente existiert und erweitert werden kann. Wiederkehrende Inhalte auf neuen
Seiten → als Komponente extrahieren.
- **Helpers** (`app/helpers.php`): `e()` (Escaping — IMMER für dynamische Ausgaben), `url()`, `asset()`
(Versionierung via filemtime), `json_load('name')` (liest `data/name.json`, static cache), `component()`.
(Versionierung via filemtime), `json_load('name')` (liest `data/name.json`, static cache), `component()`,
`icon('name', 'klasse', 'label?')` (Inline-SVG aus `public/assets/icons/`, siehe Abschnitt Icons).
- **CSS:** 6 Dateien als `<link>` in fester Reihenfolge: tokens → reset → base → layout → components →
utilities. Kein `@import`, kein Inline-Style. Komponenten-Styles in `components.css` mit
Banner-Kommentaren (`/* === hero === */`).
@@ -61,6 +63,17 @@ Per-Page-Meta (Title/Description/OG) lebt in der jeweiligen Page-Datei (`app/pag
## Formulare
- **Felder ausschließlich über `component('form-field', ['field' => …])`** (`app/components/form-field.php`) —
nie Feld-Markup von Hand. Die Komponente liefert Label, Pflichtfeld-Stern, Hilfetext und das
Fehler-Element `#<id>-error` (per `aria-describedby` verknüpft) in einheitlichem Markup.
- **Jedes neue Formular braucht:** (1) Status-Region `role="status" aria-live="polite" tabindex="-1"
data-form-status` als erstes Form-Kind, (2) Hinweiszeile „Pflichtfelder sind mit * markiert“,
(3) Honeypot-Feld `website` (visually-hidden) + Hidden-Field `ft` mit `form_token()`,
(4) serverseitige Action unter `app/actions/` mit Whitelist-Validierung + PRG-Redirect.
- `form.js` übernimmt automatisch Inline-Feldfehler (Browser-Validierungsmeldung, `aria-invalid`,
Fokus aufs erste ungültige Feld) — Konvention dafür ist die `#<id>-error`-ID aus der Komponente.
- A11y-Vorgaben: Feldränder mit `--clr-glass-border-strong` (≥3:1 Kontrast, WCAG 1.4.11), Checkbox
min. 24px (WCAG 2.5.8), Status-Region nie per `display:none` verstecken (fliegt aus dem Accessibility-Tree).
- Versand via PHPMailer über Brevo SMTP (`smtp-relay.brevo.com:587`, STARTTLS), Config aus `config/config.php`.
- Spam-Schutz ohne externe Dienste: Honeypot-Feld + HMAC-signierter Timestamp (Time-Trap, `app_secret`)
+ serverseitige Validierung mit Whitelists. POST-Redirect-GET; `form.js` macht optional fetch.
@@ -74,6 +87,13 @@ Per-Page-Meta (Title/Description/OG) lebt in der jeweiligen Page-Datei (`app/pag
- Rot nie für Fließtext auf dunklem Grund (Kontrast!) — nur als Button-/Akzent-Fläche mit weißem Text.
- Alle Werte als CSS Custom Properties in `tokens.css`; Breakpoints: 1249 (Burger) / 1024 / 992 / 768 / 600 / 500.
## Icons
- **Bootstrap Icons** (MIT), lokal & self-hosted unter `public/assets/icons/<name>.svg` — die erlaubte Icon-Quelle. Kein Icon-Font, kein CDN, keine gezeichneten CSS-/Unicode-Icons.
- Ausgabe **nur** über den Helper: `icon('pause-fill')` (dekorativ, `aria-hidden`), `icon('pause-fill', 'meine-klasse')` (mit Klasse), `icon('envelope', '', 'E-Mail')` (semantisch, `role="img"` + `aria-label`). Inline-SVG, currentColor erbt die Schriftfarbe.
- Größe = `font-size` des Elements (Icon ist `1em`), Farbe = `currentColor`. Basisklasse `.icon` in `utilities.css`.
- Nur tatsächlich genutzte Icons werden abgelegt (kein Komplett-Set). Neues Icon: `php bin/icons-add.php <name>` (Name = Dateiname auf icons.getbootstrap.com ohne `.svg`).
## Checkliste für jede neue Seite/Komponente
- [ ] Semantisches HTML (Landmarks, Headings-Hierarchie ohne Sprünge)