docs: add design-rules.md – binding design guidelines for AI agents

This commit is contained in:
Implementation Engineer
2026-07-10 23:08:41 +02:00
parent 90a7d7f2e0
commit 16fde9fb7d
+508
View File
@@ -0,0 +1,508 @@
# HMS Licht & Ton Design-Richtlinien für KI-Agenten
> **VERBINDLICH:** Diese Regeln MÜSSEN bei jeder Erweiterung der Website eingehalten werden.
> Abweichungen sind Fehler. Der freigegebene Prototyp ist die Quelle der Wahrheit.
> Prototyp: `https://webspace.media-on.de/hms-prototype/index.html?v=8`
> Referenz-Dateien: `docs/prototype-app.js`, `docs/prototype-index.html`, `frontend/assets/css/prototype-style.css`
---
## 1. Farbsystem (Dark Theme + Orange Akzent)
### Hauptfarben (Agent Zero Dark Theme Grautöne)
| Token | Hex | Verwendung |
|-------|-----|------------|
| `--bg` | `#131313` | Seiten-Hintergrund |
| `--panel` | `#1a1a1a` | Panels, Cards, Header |
| `--surface` | `#212121` | Input-Felder, Surface-Level |
| `--row` | `#272727` | List-Items, Zwischenebenen |
| `--card` | `#2d2d2d` | Card-Hintergrund |
| `--border` | `#444444a8` | Borders (mit Alpha) |
| `--border-strong` | `#555555` | Strong Borders (Inputs) |
| `--secondary` | `#656565` | Sekundärfarbe, Icons, grauer Text |
| `--primary` | `#737a81` | Primärfarbe (leicht bläuliches Grau) |
| `--text` | `#ffffff` | Haupttext |
| `--text-muted` | `#d4d4d4` | Gedimmter Text |
### Orange Akzent (DEZENT keine großen Flächen!)
| Token | Wert | Verwendung |
|-------|------|------------|
| `--color-accent` | `#EC6925` | Buttons, Badges, Text-Akzente, Logo-Border |
| `--color-accent-hover` | `#d4581a` | Button Hover |
| `--color-accent-light` | `rgba(236, 105, 37, 0.08)` | Badge-Hintergrund, Nav-Active |
| `--color-accent-border` | `rgba(236, 105, 37, 0.25)` | Card Hover Border |
| `--color-accent-dark` | `#b8461a` | Dunkle Variante |
### Status-Farben
| Token | Hex/RGBA | Verwendung |
|-------|----------|------------|
| `--color-success` | `#4ade80` | Erfolg-Meldungen |
| `--color-success-bg` | `rgba(74, 222, 128, 0.08)` | Erfolg-Hintergrund |
| `--color-error` | `#f87171` | Fehler, Pflichtfelder |
| `--color-error-bg` | `rgba(248, 113, 113, 0.08)` | Fehler-Hintergrund |
| `--color-warning` | `#fbbf24` | Warnungen |
| `--color-info` | `#60a5fa` | Info |
### FARB-REGELN
- **Orange NIEMALS als große Fläche** nur für Buttons, Badges, kleine Text-Highlights, Border
- **Hintergründe IMMER Grautöne** (#131313 bis #2d2d2d)
- **Text IMMER `var(--text)` (#ffffff) oder `var(--text-muted)` (#d4d4d4)**
- **Sekundärer Text `var(--secondary)` (#656565)** für sehr gedimmte Infos
- **Inline Styles mit CSS Variablen:** `style="color: var(--text)"`, `style="background: var(--panel)"`
---
## 2. Typography
- **Font Family:** Inter (Google Fonts), Weights: 400, 500, 600, 700, 800
- **Größen:** Tailwind Utilities (`text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `text-3xl`, `text-4xl`, `text-5xl`, `text-6xl`)
- **Headings:** `font-bold` oder `font-semibold`, `leading-tight` für große Headlines
- **Body:** `leading-relaxed` für längere Texte
- **Line Height:** 1.6 (Body), 1.1-1.3 (Headings)
---
## 3. Layout-System
### Container
- **Max-Width:** `max-w-7xl` (1280px)
- **Padding:** `px-4 sm:px-6 lg:px-8`
- **Center:** `mx-auto`
### Sections
- **Vertical Padding:** `py-12 sm:py-16` (Standard), `py-16 sm:py-20` (große Sections), `py-20 sm:py-32` (Hero)
- **Background Alternation:** `var(--bg)``var(--panel)``var(--bg)` für visuelle Trennung
- **Section Dividers:** `.hms-section-divider` (subtiler Gradient)
### Grid
- **Cards Grid:** `grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6`
- **Speaker Grid:** `.hms-speaker-grid` (auto-fill, minmax 160px)
- **Gallery Grid:** `.hms-gallery` (auto-fill, minmax 300px)
- **2-Spalten Layout:** `grid lg:grid-cols-2 gap-8` oder `gap-12`
### Responsive Breakpoints
- `sm:` 640px (Tablet Portrait)
- `md:` 768px (Desktop Nav visible, Mobile Burger hidden)
- `lg:` 1024px (Desktop, 3-4 Column Grids)
- **Mobile-First:** Alle Layouts starten 1-Spalte
---
## 4. CSS Komponenten-Klassen (hms-*)
### ÜBERSICHT: Alle hms-* Klassen aus prototype-style.css
Diese Klassen sind in `assets/css/main.css` definiert und MÜSSEN verwendet werden.
Keine eigenen Klassen erfinden!
### Buttons
```html
<!-- Primary: Orange bg, white text -->
<button class="hms-btn hms-btn-primary">Button Text</button>
<!-- Secondary: Surface bg, muted text, border -->
<button class="hms-btn hms-btn-secondary">Button Text</button>
<!-- Ghost: Transparent, muted text -->
<button class="hms-btn hms-btn-ghost">Button Text</button>
```
- **Padding:** `px-8 py-4` für große Buttons, `px-4 py-2` für kleine, `px-3 py-1.5` für Mini
- **Font:** `font-semibold` oder `font-medium`, `text-sm` für Standard
- **Hover-Effekte:** Primary → box-shadow glow + translateY(-1px); Secondary → border accent
- **Disabled:** `--secondary` bg, `cursor: not-allowed`
### Cards
```html
<div class="hms-card p-6">
<h3 class="text-lg font-semibold mb-2" style="color: var(--text)">Title</h3>
<p class="text-sm leading-relaxed" style="color: var(--text-muted)">Description</p>
</div>
```
- **Background:** `var(--panel)` (#1a1a1a)
- **Border:** `1px solid var(--border)` (#444444a8)
- **Border-Radius:** `4px` (--radius-lg)
- **Hover:** Box-shadow wird stärker, Border wird heller
### Equipment Cards
```html
<div class="hms-eq-card" @click="..." role="article" tabindex="0">
<div class="aspect-[4/3] flex items-center justify-center relative overflow-hidden" style="background: var(--surface)">
<img v-if="item.image" :src="item.image" class="absolute inset-0 w-full h-full object-cover" />
<div v-else class="text-4xl" style="color: var(--secondary)">📦</div>
<span v-if="item.category" class="hms-badge hms-badge-primary absolute top-3 left-3">{{ item.category }}</span>
</div>
<div class="p-4">
<h3 class="font-semibold text-sm mb-1 truncate" style="color: var(--text)">{{ item.name }}</h3>
<p class="text-xs mb-3 line-clamp-2" style="color: var(--secondary)">{{ item.description }}</p>
<div class="flex items-center justify-between">
<span class="text-xs" style="color: var(--secondary)">{{ item.code }}</span>
<button class="hms-btn hms-btn-ghost text-xs px-3 py-1.5" style="color: var(--color-accent)">+ Hinzufügen</button>
</div>
</div>
</div>
```
### Badges
```html
<span class="hms-badge hms-badge-primary">Badge Text</span>
<span class="hms-badge hms-badge-gray">Gray Badge</span>
<span class="hms-badge hms-badge-success">Success</span>
<span class="hms-badge hms-badge-error">Error</span>
```
- **Border-Radius:** `9999px` (pill shape)
- **Primary:** Orange bg (12% alpha), orange text, orange border (20% alpha)
### Inputs
```html
<input class="hms-input" type="text" placeholder="..." />
<input class="hms-input hms-input-error" /> <!-- mit Fehler -->
<select class="hms-input"><option>...</option></select>
<textarea class="hms-input" rows="5"></textarea>
```
- **Background:** `var(--surface)` (#212121)
- **Border:** `1px solid var(--border-strong)` (#555555)
- **Focus:** Orange border + subtle orange box-shadow
- **Error:** Red border + red box-shadow
- **Padding:** `var(--space-md) var(--space-lg)`
### Filter Chips
```html
<button class="hms-chip" :class="{ active: isActive }">Filter</button>
```
- **Inactive:** Surface bg, muted text, border
- **Active:** Orange bg, white text, orange border
### Navigation Buttons
```html
<button class="hms-nav-btn px-4 py-2 rounded-lg text-sm font-medium"
:style="isActive ? { color: 'var(--color-accent)', background: 'var(--color-accent-light)' } : { color: 'var(--text-muted)' }"
:aria-current="isActive ? 'page' : undefined">
Nav Item
</button>
```
- **Hover:** Orange text + accent-light bg + Unterstrich-Animation
- **Active:** Orange text + accent-light bg
### Skeleton Loading
```html
<div class="hms-skeleton h-4 w-3/4"></div>
```
- Shimmer Animation von `prototype-style.css`
### Spinner
```html
<span class="hms-spinner" style="width:18px;height:18px;border-width:2px"></span>
```
### Icon Circle (Service Icons)
```html
<div class="hms-icon-circle mb-4"><span aria-hidden="true">🔊</span></div>
```
- 48x48px, surface bg, border, centered content
### Hero
```html
<section class="hms-hero py-20 sm:py-32">
<div class="hms-hero-bg" style="background-image: url('...')"></div>
<div class="hms-hero-overlay"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<!-- Content -->
</div>
</section>
```
- **hms-hero-bg:** Absolute, background-size cover, opacity 0.35
- **hms-hero-overlay:** Linear gradient von rgba(19,19,19,0.6) zu var(--bg)
- **Content:** `relative z-10` über Overlay
---
## 5. Inline Style Pattern
### REGEL: Inline Styles verwenden IMMER CSS Variablen
```html
<!-- RICHTIG -->
<h1 style="color: var(--text)">Title</h1>
<p style="color: var(--text-muted)">Text</p>
<div :style="{ background: 'var(--panel)', borderColor: 'var(--border)' }">
<!-- FALSCH -->
<h1 style="color: #ffffff">Title</h1>
<p style="color: #d4d4d4">Text</p>
```
### Häufige Inline Styles aus dem Prototyp
- `style="color: var(--text)"` Haupttext
- `style="color: var(--text-muted)"` Gedimmter Text
- `style="color: var(--secondary)"` Sehr gedimmter Text
- `style="color: var(--color-accent)"` Orange Akzent
- `style="background: var(--panel)"` Panel BG
- `style="background: var(--bg)"` Seiten BG
- `style="background: var(--surface)"` Surface BG
- `:style="{ borderColor: 'var(--border)' }"` Border Color
- `:style="{ background: 'var(--color-success-bg)', color: 'var(--color-success)' }"` Success State
---
## 6. Komponenten-Struktur
### Bestehende Komponenten (NICHT neu erfinden)
| Komponente | Datei | Zweck |
|---|---|---|
| HmsLogo | `components/HmsLogo.vue` | Echtes SVG Logo (H + orange Border) |
| SpeakerIcon | `components/SpeakerIcon.vue` | 6 Lautsprecher-Typ SVG Icons |
| AppHeader | `components/AppHeader.vue` | Sticky Header, Nav, Cart, Mobile Burger |
| AppFooter | `components/AppFooter.vue` | 4-Spalten Footer |
| ServiceCard | `components/ServiceCard.vue` | Service Card mit Icon Circle |
| EquipmentCard | `components/EquipmentCard.vue` | Equipment Card mit Badge, Image, Add-to-Cart |
| LoadingSkeleton | `components/LoadingSkeleton.vue` | Skeleton Loading Cards |
| EmptyState | `components/EmptyState.vue` | Empty State mit Icon + Action |
| ErrorState | `components/ErrorState.vue` | Error State mit Retry |
### Neue Komponenten erstellen Checklist
1. CSS Klassen aus `prototype-style.css` verwenden (hms-*)
2. Inline Styles mit CSS Variablen
3. Tailwind Utilities für Layout/Spacing
4. TypeScript mit `lang="ts"`
5. `useHead()` für Meta Tags
6. ARIA Attributes (role, aria-label, aria-current, etc.)
7. Responsive (mobile-first, sm/md/lg breakpoints)
---
## 7. UX States (MUSS in jeder View implementiert)
### Loading State
```html
<loading-skeleton :count="6" />
```
### Empty State
```html
<empty-state icon="🔍" title="Keine Ergebnisse" message="..." action-label="Zurück" @action="..." />
```
### Error State
```html
<error-state title="Fehler" message="..." @retry="..." />
```
### Success State
```html
<div class="text-center py-8" role="status">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full mb-4"
:style="{ background: 'var(--color-success-bg)', color: 'var(--color-success)' }">
<svg class="w-8 h-8" ...></svg>
</div>
<h3 class="text-lg font-semibold mb-2" style="color: var(--text)">Erfolg</h3>
<p class="text-sm mb-6" style="color: var(--secondary)">Nachricht</p>
</div>
```
### Submitting State
```html
<button :disabled="submitting" class="hms-btn hms-btn-primary">
<span v-if="submitting" class="hms-spinner" style="width:18px;height:18px;border-width:2px"></span>
<span v-else>Senden</span>
</button>
```
---
## 8. Accessibility (WCAG 2.1 AA)
### MUSS-Regeln
- **Skip Link:** `<a class="skip-link" href="#main">Zum Inhalt springen</a>` am Seitenanfang
- **Semantic HTML:** `<header>`, `<main>`, `<footer>`, `<nav>`, `<section>`, `<article>`
- **ARIA:** `role`, `aria-label`, `aria-current="page"`, `aria-expanded`, `aria-invalid`, `aria-describedby`, `role="alert"`, `role="status"`
- **Focus Visible:** `*:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }`
- **Form Labels:** Jedes Input hat `<label for="...">`, Pflichtfelder mit `<span style="color: var(--color-error)">*</span>`
- **Heading Hierarchy:** h1 > h2 > h3, keine Levels überspringen
- **Keyboard Navigation:** Alle interaktiven Elemente per Tab erreichbar, `tabindex="0"` auf klickbare Cards
- **Alt Text:** Alle Bilder haben `alt="..."`
- `aria-hidden="true"` auf dekorativen Icons
- `loading="lazy"` auf alle Bilder
---
## 9. Meta Tags & SEO
### MUSS pro Seite
```typescript
useHead({
title: 'Seite HMS Licht & Ton',
meta: [
{ name: 'robots', content: 'noindex, nofollow, noarchive, nosnippet' },
],
});
```
### JSON-LD (nur Home)
- `LocalBusiness` Schema mit Adresse, Öffnungszeiten, Services
### OpenGraph
- `og:type`, `og:title`, `og:description`, `og:locale`, `og:site_name`, `og:url`
### REGELN
- **noindex** auf ALLEN Seiten (Domain nicht öffentlich indexiert)
- **KEINE sitemap.xml**
- Inter Font via Google Fonts preconnect
- `theme-color: #EC6925` (nur im Head, nicht als Design-Fläche!)
---
## 10. Bilder
### Hero Background
- Unsplash Konzert-Bild: `background-image: url('https://images.unsplash.com/photo-1470229722913-7c0e2dbbafd3?w=1920&q=80')`
- **Opacity:** 0.35 (über `.hms-hero-bg`)
- **Overlay:** Linear gradient zu `var(--bg)`
### Referenzen
- 9 echte HMS-Fotos: `img/ref1.jpg` bis `img/ref9.jpg`
- **Hover:** `group-hover:scale-110` (Zoom-Effekt)
- **Gradient Overlay:** `bg-gradient-to-t from-black/50 to-transparent` bei Hover
### Equipment
- Platzhalter `📦` Emoji wenn kein Bild vorhanden
- `loading="lazy"` auf allen Bildern
### About Section
- Unsplash Setup-Bild: `https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?w=800&q=80`
---
## 11. Spacing & Radius
### Spacing Tokens
| Token | Wert |
|-------|-------|
| `--space-xs` | 0.25rem (4px) |
| `--space-sm` | 0.5rem (8px) |
| `--space-md` | 1rem (16px) |
| `--space-lg` | 1.5rem (24px) |
| `--space-xl` | 2rem (32px) |
| `--space-2xl` | 3rem (48px) |
| `--space-3xl` | 4rem (64px) |
| `--space-4xl` | 6rem (96px) |
### Border Radius (MINIMAL!)
| Token | Wert |
|-------|-------|
| `--radius-sm` | 2px |
| `--radius-md` | 3px |
| `--radius-lg` | 4px |
| `--radius-xl` | 4px |
| `--radius-full` | 9999px (nur Badges, Pills) |
### Shadows
| Token | Wert |
|-------|-------|
| `--shadow-sm` | `0 1px 2px 0 rgb(0 0 0 / 0.3)` |
| `--shadow-md` | `0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3)` |
| `--shadow-lg` | `0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3)` |
| `--shadow-xl` | `0 20px 25px -5px rgb(0 0 0 / 0.5), 0 8px 10px -6px rgb(0 0 0 / 0.3)` |
---
## 12. Transitions
| Token | Dauer |
|-------|-------|
| `--transition-fast` | 150ms ease |
| `--transition-base` | 250ms ease |
| `--transition-slow` | 400ms ease |
---
## 13. Build & Test
### Frontend
```bash
cd frontend && npm run build # Build (MUSS 0 errors)
cd frontend && npx vitest run # Unit Tests (MUSS alle pass)
cd frontend && npx playwright test --grep 'Pattern' # E2E Tests
curl -s http://localhost:3000/ # Smoke Test
```
### Backend
```bash
cd backend && python -m pytest tests/ -v --cov=app # Tests (MUSS alle pass)
curl -s http://localhost:8000/api/health # Health Check
```
### Deployment
- Server: coolify-01 (46.225.91.159)
- Pfad: `/data/hms-licht-ton/`
- Branch: `feature/T04-rentman-integration` (oder `main` nach Merge)
- Docker Compose: `docker compose up -d`
- Domain: `https://hms.media-on.de` (Traefik + Let's Encrypt)
---
## 14. VERBOTEN
- ❌ Eigene CSS Klassen erfinden (nur `hms-*` verwenden)
- ❌ Generische `.btn-primary` oder `.nav-link` Klassen
- ❌ Hardcoded Hex-Werte in Inline Styles (immer `var(--...)`)
- ❌ Fake-Logo (immer echtes SVG aus `assets/logo.svg`)
- ❌ Große Orange-Flächen
- ❌ Abgerundete Ecken > 4px (außer Badges/Pills = 9999px)
- ❌ Client-only rendering für SSR Pages (`/mietkatalog`, `/mietkatalog/**`)
- ❌ Fehlende ARIA Attributes
- ❌ Fehlende UX States (Loading, Empty, Error, Success)
- ❌ Fehlendes `noindex` Meta Tag
- ❌ Eigene Templates die nicht dem Prototyp entsprechen
---
## 15. Referenz-Dateien
| Datei | Zweck |
|---|---|
| `docs/prototype-app.js` | Original Vue 3 SPA Prototyp (761 Zeilen, 19 Komponenten) |
| `docs/prototype-index.html` | Original HTML mit Meta Tags, JSON-LD |
| `frontend/assets/css/prototype-style.css` | Original CSS mit allen hms-* Klassen (13KB) |
| `frontend/assets/css/main.css` | Aktive CSS (enthält prototype-style.css + Tailwind directives) |
| `frontend/assets/logo.svg` | Echtes HMS Logo (503 bytes, weißes H + orange Border) |
| `frontend/public/logo.svg` | Static copy für Nuxt public dir |
| `docs/ui_design.md` | Original UI Design Dokumentation |
| `docs/design-rules.md` | DIES DATEI Design Richtlinien für KI-Agenten |
---
## 16. Schnell-Referenz: Neue Seite erstellen
```vue
<template>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<h1 class="text-3xl sm:text-4xl font-bold mb-2" style="color: var(--text)">Seiten-Titel</h1>
<p class="mb-8" style="color: var(--secondary)">Untertitel</p>
<!-- Loading -->
<loading-skeleton v-if="loading" :count="6" />
<!-- Error -->
<error-state v-else-if="error" title="Fehler" message="..." @retry="loadData" />
<!-- Content -->
<div v-else class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div v-for="item in items" :key="item.id" class="hms-card p-6">
<h3 class="text-lg font-semibold mb-2" style="color: var(--text)">{{ item.title }}</h3>
<p class="text-sm leading-relaxed" style="color: var(--text-muted)">{{ item.description }}</p>
<button class="hms-btn hms-btn-primary mt-4">Aktion</button>
</div>
</div>
<!-- Empty -->
<empty-state v-if="!loading && !error && items.length === 0"
icon="🔍" title="Keine Daten" message="..." action-label="Zurück" @action="..." />
</div>
</template>
<script setup lang="ts">
useHead({
title: 'Seite HMS Licht & Ton',
meta: [{ name: 'robots', content: 'noindex, nofollow, noarchive, nosnippet' }],
});
</script>
```
---
*Dieses Dokument ist die verbindliche Design-Richtlinie für alle Erweiterungen der HMS Licht & Ton Website.*