Files
votianlt/UI_GUIDELINES.md

1021 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VotianLT UI Theme & Gestaltungsrichtlinien
> Gilt für alle Views unter `src/main/java/de/assecutor/votianlt/pages/view/`
> Theme-Datei: `src/main/frontend/themes/votian-modern/styles.css`
---
## 1. Design-Prinzipien
- **Konsistenz**: Alle Views nutzen dieselben Klassen, Abstände und Farben.
- **Kein Overflow**: Kein View darf horizontal scrollen. Alle Container haben `box-sizing: border-box`, `max-width: 100%` und `min-width: 0`.
- **Kein externes Margin/Padding am View-Root**: Views füllen ihren Container vollständig aus. Abstände zum Bildschirmrand entstehen ausschließlich durch den `view-container`.
- **Lumo-Overrides**: Vaadin-Lumo-Standardstile werden durch gezielte CSS-Regeln mit höherer Spezifizität überschrieben **nicht** mit inline Styles in Java-Code lösen.
---
## 2. Farben & Custom Properties
Alle Farben sind als CSS-Custom-Properties auf `html` definiert und **müssen** statt hartcodierter Hex-Werte verwendet werden.
### Hauptfarben
| Variable | Wert | Verwendung |
|---|---|---|
| `--app-accent` | `#2563eb` | Primäraktionen, Links, Highlights |
| `--app-accent-strong` | `#1d4ed8` | Hover-Zustand von Primärbuttons |
| `--app-accent-soft` | `rgba(37,99,235,0.12)` | Selektierter Zeilenhintergrund |
| `--app-success` | `#059669` | Erfolgsstatus, abgeschlossene Aufgaben |
| `--app-warning` | `#d97706` | Warnungen |
| `--app-danger` | `#dc2626` | Fehler, kritische Zustände |
### Oberflächen & Hintergründe
| Variable | Wert | Verwendung |
|---|---|---|
| `--app-surface` | `rgba(255,255,255,0.78)` | Karten, Panels (Glasmorphismus) |
| `--app-surface-solid` | `#ffffff` | Volldeckende Flächen |
| `--app-surface-muted` | `rgba(247,250,255,0.88)` | Gedämpfte Panels |
| `--app-shell-background` | Radial+Linear-Gradient | Seitenhintergrund (Body) |
| `--app-sidebar-background` | Linear-Gradient dunkelblau | Drawer/Sidebar |
### Text
| Variable | Wert | Verwendung |
|---|---|---|
| `--app-text-strong` | `#0f172a` | Überschriften, wichtige Labels |
| `--app-text` | `#1e293b` | Fließtext |
| `--app-text-muted` | `#64748b` | Sekundärtext, Beschreibungen |
### Ränder
| Variable | Wert | Verwendung |
|---|---|---|
| `--app-border` | `rgba(148,163,184,0.18)` | Subtile Trennlinien |
| `--app-border-strong` | `rgba(148,163,184,0.28)` | Sichtbare Kartenränder |
| `--app-field-border` | `#d6dde7` | Formularfeld-Rand |
| `--app-field-border-hover` | `#c6d0dd` | Formularfeld-Rand (Hover) |
### Schatten
| Variable | Wert | Verwendung |
|---|---|---|
| `--app-shadow-sm` | `0 12px 28px rgba(15,23,42,0.08)` | Kleine Karten |
| `--app-shadow-md` | `0 20px 44px rgba(15,23,42,0.10)` | Mittlere Panels |
| `--app-shadow-lg` | `0 28px 72px rgba(15,23,42,0.12)` | Surface-Panels |
| `--app-shadow-xl` | `0 36px 88px rgba(8,18,36,0.24)` | Hero-Bereiche, Dialoge |
---
## 3. Typografie
**Schriftart**: `Manrope` (Google Fonts, Gewichte 400800)
**Fallbacks**: `Avenir Next`, `Segoe UI`, `sans-serif`
Lumo-Überschreibungen:
```css
--lumo-font-family: "Manrope", "Avenir Next", "Segoe UI", sans-serif;
--lumo-base-color: #f5f7fb;
```
Alle `h1``h6` haben `letter-spacing: -0.03em` und `color: var(--app-text-strong)`.
### Responsive Schriftgrößen (`clamp`)
| Element | Wert |
|---|---|
| `.form-title` | `clamp(1.55rem, 3vw, 2.2rem)`, font-weight 800 |
| `.section-title` | `clamp(1.4rem, 3.4vw, 2rem)`, font-weight 800 |
| `.hero-panel-title` | `clamp(2rem, 5vw, 3.4rem)`, font-weight 800 |
| `.dashboard-title` | `clamp(1.7rem, 3.2vw, 2.5rem)`, font-weight 800 |
| `.login-card-title` | `clamp(1.8rem, 4vw, 2.4rem)`, font-weight 800 |
**Regel**: Überschriften mit `font-weight: 800` und `letter-spacing: -0.05em` bis `-0.06em` für Großtitel.
---
## 4. Layout-Struktur (App Shell)
```
vaadin-app-layout (336px Drawer)
├── [part=drawer] ← Sidebar (Dunkelblau-Gradient)
│ ├── .app-drawer-header
│ ├── .app-drawer-scroll (Scroller, flex: 1)
│ │ └── .app-nav-container
│ │ └── .app-nav-tree (TreeGrid)
│ └── .app-user-menu
└── [Light DOM default slot]
└── .view-container ← Routed View Container
└── <geroutete View>
```
### `view-container`
Der Container, in dem alle gerouteten Views dargestellt werden:
```css
.view-container {
display: flex;
flex-direction: column;
margin: 20px; /* Abstand zum Browserrand */
width: calc(100% - 40px);
height: calc(100dvh - 40px);
overflow: hidden;
border-radius: 12px;
box-sizing: border-box;
}
```
**Wichtig**: Views, die in `.view-container` landen, erhalten automatisch:
```css
flex: 1 1 0; min-height: 0; min-width: 0; max-width: 100%; box-sizing: border-box;
```
---
## 5. View-Klassen
Jede View muss **genau eine** der folgenden Root-Klassen tragen:
| Klasse | Verwendung | Extends |
|---|---|---|
| `data-view` | Listen, Tabellen, Grids | `Main` oder `VerticalLayout` |
| `form-page` | Formulare mit zentriertem Inhalt | `VerticalLayout` |
| `admin-form-view` | Admin-Formulare | `VerticalLayout` |
| `message-hub-view` | Nachrichten-Übersicht | `Main` |
| `statistics-chat-view` | Statistik/Chat | `VerticalLayout` |
| `dashboard-view` | Dashboard-Seiten | `VerticalLayout` |
| `landing-view` | Startseite (unauthentifiziert) | `Main` |
| `login-view` | Login-Seite | `Main` |
### Kritische Java-Regeln
```java
// ✅ Richtig für Daten-Views:
addClassName("data-view");
// KEIN setJustifyContentMode(CENTER) — wird per CSS auf START erzwungen
// ✅ Richtig für Formular-Views (zentrierter Inhalt):
addClassName("form-page");
setJustifyContentMode(JustifyContentMode.CENTER);
setDefaultHorizontalComponentAlignment(Alignment.CENTER);
// ❌ Falsch: setPadding(true) auf View-Root oder form-shell ohne form-card
// → wird per CSS auf 0 zurückgesetzt, erzeugt keine visuelle Wirkung
```
---
## 6. Interne Layout-Klassen
### `form-shell` Vollbreite-Wrapper
```java
VerticalLayout layout = new VerticalLayout();
layout.setPadding(false);
layout.setSpacing(true);
layout.setWidthFull();
layout.addClassName("form-shell");
```
**Regeln**:
- Kein `setPadding(true)` → hat keine Wirkung, da via CSS überschrieben
- Kein horizontales Margin
- `box-sizing: border-box`, `max-width: 100%`, `min-width: 0`
### `form-card` Schmales zentriertes Karten-Formular
```java
container.addClassNames("form-shell", "narrow", "form-card");
container.setWidth("400px");
container.setMaxWidth("100%");
```
Bekommt **weißen Hintergrund** (0.9 Alpha), `border-radius: 28px`, `backdrop-filter: blur(18px)`.
### `surface-panel` Datenpanel / Tabellenkarte
```java
Div panel = new Div(grid);
panel.addClassNames("surface-panel", "data-grid-panel");
panel.setWidthFull();
```
```css
.surface-panel {
border: 1px solid var(--app-border-strong);
background: var(--app-surface);
backdrop-filter: blur(18px);
box-shadow: var(--app-shadow-lg);
border-radius: 28px;
box-sizing: border-box;
max-width: 100%;
}
.data-grid-panel {
padding: 0.8rem;
min-height: 420px;
box-sizing: border-box;
max-width: 100%;
}
```
---
## 7. ViewToolbar
Einheitliche Toolbar für alle Seiten-Überschriften:
```java
add(new ViewToolbar(getTranslation("my.view.title")));
// Mit Aktionsbutton:
add(new ViewToolbar(getTranslation("my.view.title"), myButton));
```
```css
.view-toolbar {
width: 100%;
padding: 0.75rem 1rem;
box-sizing: border-box;
}
.view-toolbar-title {
color: var(--app-text-strong);
font-weight: 800;
letter-spacing: -0.05em;
}
```
---
## 8. Daten-Grids
Standard-Muster für alle Tabellen-Views:
```java
// View-Root
setSizeFull();
addClassName("data-view");
// ViewToolbar
add(new ViewToolbar(getTranslation("title"), addButton));
// Grid
Grid<T> grid = new Grid<>(T.class, false);
grid.setWidthFull();
grid.setHeightFull();
grid.addClassName("data-grid");
// Panel-Wrapper
Div panel = new Div(grid);
panel.addClassNames("surface-panel", "data-grid-panel");
panel.setWidthFull();
add(panel);
```
**Grid-Styling**:
- Hintergrund: `rgba(255,255,255,0.88)`, `border-radius: 24px`
- Header: Gedämpftes Grau, Uppercase, `font-weight: 800`, `font-size: 0.76rem`
- Selektierte Zeile: `--app-accent-soft` Hintergrund
---
## 9. Formulare (Form-Views)
```java
// View-Root
setSizeFull();
setJustifyContentMode(JustifyContentMode.CENTER);
setDefaultHorizontalComponentAlignment(Alignment.CENTER);
setPadding(true);
addClassName("form-page");
// Formular-Container
VerticalLayout container = new VerticalLayout();
container.setWidth("600px");
container.setMaxWidth("100%");
container.setPadding(true);
container.setSpacing(true);
container.addClassNames("form-shell", "form-card");
// Formular-Felder
FormLayout form = new FormLayout();
form.setWidthFull();
form.setResponsiveSteps(new FormLayout.ResponsiveStep("0", 2));
```
**Formularfelder** haben automatisch:
- `border-radius: 20px`
- `background: #ffffff`
- `border: 1px solid var(--app-field-border)`
- Hover: Blaue Highlight-Farbe
- Fokus: Blauer Ring `rgba(37,99,235,0.18)`
---
## 10. Filter-Panels
```java
HorizontalLayout filterBar = new HorizontalLayout();
filterBar.addClassName("filter-panel");
filterBar.setWidthFull();
```
```css
.filter-panel {
padding: 1rem 1.15rem;
border-radius: 26px;
flex-wrap: wrap;
gap: 1rem;
min-width: 0;
max-width: 100%;
box-sizing: border-box;
}
```
**Wichtig**: Filter-Felder **niemals** mit fixen Pixel-Breiten versehen → `flex-wrap: wrap` und `min-width: 0` sorgen für korrektes Responsive-Verhalten.
---
## 11. Sidebar / Navigation
### Nav-Rows
```css
.app-nav-row {
border-radius: 18px;
border: 1px solid rgba(255,255,255,0.06);
background: rgba(255,255,255,0.08);
max-width: calc(100% - 4px);
transition: transform 0.18s ease, background-color 0.18s ease;
}
.app-nav-row:hover {
transform: translateX(4px);
background: rgba(255,255,255,0.14);
}
```
### User Menu
```css
.app-user-menu {
margin: auto 0.5rem 0.5rem;
padding: 0.45rem 0.65rem;
border-radius: 22px;
border: 1px solid rgba(255,255,255,0.1);
background: rgba(255,255,255,0.08);
backdrop-filter: blur(14px);
}
```
---
## 12. Karten-Hierarchie
| Klasse | Radius | Hintergrund | Einsatz |
|---|---|---|---|
| `surface-panel` | 28px | rgba(255,255,255,0.78) + Blur | Hauptdatenpanels |
| `form-card` | 28px | rgba(255,255,255,0.90) + Blur | Formular-Cards |
| `section-card`, `content-card` | 26px | rgba(255,255,255,0.88) | Abschnittspanels |
| `simple-card` | 26px | rgba(255,255,255,0.88) | Einfache Karten |
| `detail-card` | 24px | rgba(255,255,255,0.88) | Detail-Informationen |
| `hero-panel` | 34px | Dunkelblau-Gradient | Hero-Bereiche |
| `feature-card` | 24px | Linear-Gradient weiß/blau | Feature-Kacheln |
| `message-card` | 22px | Linear-Gradient weiß | Nachrichten |
| `station-tile` | 24px | Linear-Gradient weiß | Station-Kacheln |
| `job-task-card` | 22px | Linear-Gradient weiß | Aufgaben-Karten |
---
## 13. Station Tiles & Kacheln
### Station Tile
```css
.station-tile {
border-radius: 24px;
border: 1px solid var(--app-border-strong);
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(244,248,255,0.9));
box-shadow: var(--app-shadow-sm);
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.station-tile:hover {
transform: translateY(-3px);
box-shadow: var(--app-shadow-md);
border-color: rgba(37,99,235,0.24);
}
.station-tile.validated {
background: linear-gradient(180deg, rgba(236,253,245,0.98), rgba(209,250,229,0.88));
border-color: rgba(5,150,105,0.26);
}
```
### Add Station Tile (gestrichelt)
```css
.add-station-tile {
background: linear-gradient(180deg, rgba(241,245,249,0.9), rgba(248,250,252,0.98));
border-style: dashed;
}
```
### Stations Grid
```css
.stations-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
width: 100%;
}
```
---
## 14. Job Task Cards
```css
.job-task-card {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
border: 1px solid var(--app-border-strong);
border-radius: 22px;
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,249,255,0.9));
box-shadow: var(--app-shadow-sm);
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
}
.job-task-card:hover {
transform: translateY(-2px);
box-shadow: var(--app-shadow-md);
border-color: rgba(37,99,235,0.24);
}
.job-task-card.completed {
border-color: rgba(5,150,105,0.22);
background: linear-gradient(180deg, rgba(236,253,245,0.98), rgba(220,252,231,0.88));
}
```
### Task Status Badges
```css
.job-task-status.open {
background: rgba(220,38,38,0.12);
color: var(--lumo-error-text-color);
}
.job-task-status.completed {
background: rgba(5,150,105,0.12);
color: var(--lumo-success-text-color);
}
```
---
## 15. Dashboard Stat Cards
```css
.dashboard-stat-card {
position: relative;
border-radius: 24px;
padding: 1.15rem;
border: 1px solid var(--app-border-strong);
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,249,255,0.92));
box-shadow: var(--app-shadow-md);
}
.dashboard-stat-card::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 4px;
background: var(--dashboard-accent, var(--app-accent));
}
```
### Akzent-Farben
```css
.accent-blue { --dashboard-accent: #2563eb; }
.accent-green { --dashboard-accent: #059669; }
.accent-purple { --dashboard-accent: #7c3aed; }
.accent-orange { --dashboard-accent: #d97706; }
.accent-gray { --dashboard-accent: #64748b; }
.accent-red { --dashboard-accent: #dc2626; }
```
---
## 16. Message & Chat Components
### Message Card
```css
.message-card {
padding: 1rem;
border: 1px solid var(--app-border-strong);
border-radius: 22px;
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,249,255,0.9));
box-shadow: var(--app-shadow-sm);
transition: transform 0.22s ease, box-shadow 0.22s ease;
}
.message-card:hover {
transform: translateY(-4px);
box-shadow: 0 24px 48px rgba(15,23,42,0.14);
}
```
### Chat Bubbles
```css
.message-bubble {
max-width: min(78%, 720px);
padding: 0.85rem 1rem;
border-radius: 22px;
box-shadow: var(--app-shadow-sm);
}
.message-bubble.client {
background: rgba(255,255,255,0.96);
border: 1px solid var(--app-border-strong);
}
.message-bubble.server {
background: linear-gradient(135deg, rgba(191,219,254,0.8), rgba(167,243,208,0.74));
border: 1px solid rgba(59,130,246,0.22);
}
```
### Chat AI/User Bubbles
```css
.chat-bubble--user {
background: linear-gradient(135deg, #2563eb, #1d4ed8);
color: #eff6ff;
padding: 0.85rem 1rem;
border-radius: 24px;
}
.chat-bubble--ai {
background: rgba(255,255,255,0.94);
border: 1px solid var(--app-border-strong);
padding: 1rem;
border-radius: 24px;
}
```
---
## 17. Dialoge
```css
vaadin-dialog-overlay::part(content) {
border-radius: 28px;
border: 1px solid var(--app-border-strong);
background: rgba(255,255,255,0.94);
backdrop-filter: blur(20px);
box-shadow: var(--app-shadow-xl);
}
```
### Dialog Panels
| Klasse | Verwendung |
|---|---|
| `.dialog-form-panel` | Formulare im Dialog |
| `.dialog-content-panel` | Inhalte im Dialog |
| `.dialog-task-card` | Aufgaben-Karten im Dialog |
| `.dialog-cargo-card` | Fracht-Karten im Dialog |
```css
.dialog-form-panel,
.dialog-content-panel {
backdrop-filter: blur(12px);
padding: 1rem;
border-radius: 24px;
}
```
---
## 18. Detail Cards
```css
.detail-card {
border: 1px solid var(--app-border-strong);
border-radius: 24px;
background: rgba(255,255,255,0.9);
box-shadow: var(--app-shadow-sm);
padding: 1rem;
}
.detail-card--accent {
border-color: rgba(37,99,235,0.22);
background: linear-gradient(180deg, rgba(219,234,254,0.62), rgba(239,246,255,0.9));
}
.detail-card--code,
.detail-card--comment {
font-family: ui-monospace, "SFMono-Regular", monospace;
}
```
---
## 19. Route & Summary Cards
```css
.route-card,
.summary-card {
border: 1px solid var(--app-border-strong);
border-radius: 24px;
background: rgba(255,255,255,0.84);
box-shadow: var(--app-shadow-sm);
padding: 1rem;
}
.services-panel,
.notes-panel {
border: 1px solid var(--app-border-strong);
border-radius: 24px;
background: rgba(255,255,255,0.84);
box-shadow: var(--app-shadow-sm);
padding: 0.75rem 1rem 1rem;
}
```
---
## 20. Login Seite
```css
.login-shell {
width: min(1120px, 100%);
display: grid;
grid-template-columns: minmax(0, 1.15fr) minmax(360px, 440px);
gap: 1.5rem;
}
.login-highlight {
position: relative;
overflow: hidden;
min-height: 560px;
padding: clamp(1.8rem, 4vw, 3rem);
border-radius: 32px;
border: 1px solid rgba(255,255,255,0.14);
background: linear-gradient(140deg, #081224 0%, #173d8d 52%, #0f766e 100%);
box-shadow: var(--app-shadow-xl);
}
.login-card {
width: 100%;
padding: clamp(1.35rem, 3vw, 2rem);
border-radius: 32px;
border: 1px solid var(--app-border-strong);
background: rgba(255,255,255,0.88);
backdrop-filter: blur(20px);
box-shadow: var(--app-shadow-lg);
}
```
### Eyebrow Chip
```css
.eyebrow-chip {
display: inline-flex;
align-items: center;
padding: 0.45rem 0.8rem;
border-radius: 999px;
background: rgba(255,255,255,0.14);
color: rgba(248,250,252,0.92);
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
```
---
## 21. Landing Page
```css
.landing-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
}
.landing-logo {
font-size: clamp(1.6rem, 4vw, 2.2rem);
font-weight: 800;
letter-spacing: -0.05em;
}
.landing-nav-button,
.landing-language-button {
border-radius: 999px;
}
```
### Hero Panel
```css
.hero-panel {
position: relative;
overflow: hidden;
min-height: 340px;
justify-content: center;
text-align: center;
border-radius: 34px;
border: 1px solid rgba(255,255,255,0.14);
background: linear-gradient(135deg, #081224 0%, #1d4ed8 54%, #0f766e 100%);
box-shadow: var(--app-shadow-xl);
}
.hero-panel-title {
color: #f8fafc;
font-size: clamp(2rem, 5vw, 3.4rem);
font-weight: 800;
letter-spacing: -0.06em;
}
.hero-panel-text {
color: rgba(226,232,240,0.92);
font-size: clamp(1rem, 2vw, 1.15rem);
}
```
---
## 22. Timeline Components
```css
.timeline-entry-card {
margin-bottom: 0.75rem;
padding: 1rem;
border: 1px solid var(--app-border-strong);
border-radius: 24px;
background: rgba(255,255,255,0.9);
box-shadow: var(--app-shadow-sm);
border-left: 4px solid var(--timeline-accent, var(--app-accent));
}
.timeline-reason {
font-weight: 700;
color: var(--app-text-strong);
}
.timeline-timestamp {
color: var(--app-text-muted);
font-size: 0.78rem;
}
```
---
## 23. Invoice Components
### Invoice Generator
```css
.invoice-generator-panel {
backdrop-filter: blur(16px);
border-radius: 24px;
padding: 1rem;
border: 1px solid var(--app-border-strong);
background: rgba(255,255,255,0.9);
}
.invoice-generator-template {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.95rem 1rem;
border: 1px solid var(--app-border-strong);
border-radius: 18px;
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(246,249,255,0.92));
cursor: grab;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.invoice-generator-template:hover {
transform: translateY(-2px);
box-shadow: var(--app-shadow-sm);
}
.invoice-generator-canvas {
position: relative;
overflow: hidden;
border: 2px dashed rgba(148,163,184,0.5);
border-radius: 24px;
background:
linear-gradient(180deg, rgba(255,255,255,0.95), rgba(241,245,249,0.9)),
linear-gradient(90deg, rgba(148,163,184,0.08) 1px, transparent 1px),
linear-gradient(rgba(148,163,184,0.08) 1px, transparent 1px);
background-size: auto, 28px 28px, 28px 28px;
}
```
### Price Table
```css
.price-table {
width: 100%;
}
.price-row {
display: flex;
justify-content: space-between;
gap: 1rem;
padding: 0.35rem 0;
}
.price-row--strong .price-row-label,
.price-row--strong .price-row-value {
font-weight: 800;
color: var(--app-text-strong);
}
```
---
## 24. Buttons
```css
vaadin-button {
font-weight: 700;
border-radius: var(--lumo-border-radius-m);
transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
}
vaadin-button:not([disabled]):hover {
transform: translateY(-1px);
}
vaadin-button[theme~="primary"] {
box-shadow: 0 8px 20px rgba(37,99,235,0.22);
}
vaadin-button[theme~="primary"][theme~="success"] {
box-shadow: 0 8px 20px rgba(5,150,105,0.24);
}
vaadin-button[theme~="primary"][theme~="error"] {
box-shadow: 0 8px 20px rgba(220,38,38,0.24);
}
vaadin-button[disabled] {
opacity: 0.46;
transform: none !important;
}
```
**Pill-Buttons** (Nav, Landing): `border-radius: 999px`
---
## 25. Overflow & Box-Model Regeln
### Pflicht bei allen Container-Elementen
```java
component.getStyle()
.set("box-sizing", "border-box")
.set("max-width", "100%")
.set("min-width", "0");
```
### Warum `box-sizing: border-box` kritisch ist
Ohne `box-sizing: border-box` gilt:
```
Gesamtbreite = width + padding-left + padding-right + border-left + border-right
```
- `surface-panel` hat `border: 1px` → ohne box-sizing ist es `100% + 2px` → Overflow
- `data-grid-panel` hat `padding: 0.8rem` → ohne box-sizing ist es `100% + 1.6rem` → Overflow
### Warum `min-width: 0` kritisch ist
In Flex-Containern verhindert die Default-Eigenschaft `min-width: auto`, dass Flex-Items kleiner werden als ihr Inhalt. `min-width: 0` erlaubt das Schrumpfen unter die Inhaltsgröße.
---
## 26. Lumo-Override-Strategie
### Padding-Override
```css
/* Lumo: vaadin-vertical-layout[theme~="padding"] → Spezifizität 0,0,1,1 */
/* Override braucht mindestens 0,0,2,0: */
vaadin-vertical-layout.form-shell:not(.form-card) { padding: 0; }
```
### Inline-Style-Override
`setJustifyContentMode()` setzt Inline-Styles → nur `!important` überschreibt:
```css
vaadin-vertical-layout.data-view {
justify-content: flex-start !important;
align-items: stretch !important;
}
```
**Regel**: `!important` nur für Inline-Style-Overrides verwenden.
---
## 27. Responsive Verhalten
### Breakpoints
| Breakpoint | Änderungen |
|---|---|
| `max-width: 980px` | Login-Shell wird einspaltig |
| `max-width: 720px` | `view-container` Margin reduziert auf 10px, border-radius auf 8px |
| `max-height: 820px` | Drawer-Header und Nav-Rows bekommen kompakteres Padding |
### Mobile View-Container
```css
@media (max-width: 720px) {
.view-container {
margin: 10px;
width: calc(100% - 20px);
height: calc(100dvh - 20px);
border-radius: 8px;
}
}
```
### Flex-Wrap-Pflicht
Alle horizontalen Layouts, die Felder oder Buttons enthalten, **müssen** `flex-wrap: wrap` haben:
```java
layout.getStyle().set("flex-wrap", "wrap");
```
---
## 28. Photo Gallery
```css
.job-photo-gallery {
max-width: 600px;
min-height: 500px;
height: 500px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
background: rgba(255,255,255,0.96);
border-radius: 24px;
}
.job-photo-counter {
position: absolute;
top: var(--lumo-space-s);
right: var(--lumo-space-s);
padding: var(--lumo-space-xs) var(--lumo-space-s);
border-radius: 999px;
background: rgba(15,23,42,0.72);
color: #f8fafc;
font-weight: 700;
}
.job-photo-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
border-radius: 999px;
background: rgba(255,255,255,0.9);
box-shadow: var(--app-shadow-sm);
}
```
---
## 29. Empty State
```css
.empty-state-card {
text-align: center;
color: var(--app-text-muted);
padding: 1rem;
border: 1px dashed var(--app-border-strong);
border-radius: 24px;
background: rgba(248,250,252,0.74);
}
```
---
## 30. Inline Flash (Fehlermeldungen)
```css
.inline-flash {
width: 100%;
padding: 0.95rem 1rem;
border-radius: 18px;
border: 1px solid rgba(220,38,38,0.22);
background: rgba(254,242,242,0.92);
color: var(--lumo-error-text-color);
box-sizing: border-box;
}
```
---
## 31. Checkliste für neue Views
Beim Erstellen einer neuen View folgende Punkte prüfen:
- [ ] Root-Element hat genau eine View-Klasse (`data-view`, `form-page`, etc.)
- [ ] **Kein** `setPadding(true)` auf dem View-Root oder `form-shell`-Wrappern (ohne `form-card`)
- [ ] **Kein** `setJustifyContentMode(CENTER)` auf Daten-Views (`data-view`, `admin-form-view`)
- [ ] Alle Container haben `max-width: 100%` und `box-sizing: border-box`
- [ ] Alle Flex-Container mit wechselnden Inhalten haben `flex-wrap: wrap`
- [ ] Formularfelder haben **keine** fixen Pixel-Breiten (`setWidth("200px")` verboten)
- [ ] `ViewToolbar` ist das erste Kind-Element
- [ ] Grid-Panel verwendet `surface-panel data-grid-panel` mit `setWidthFull()`
- [ ] Schmale Formular-Container haben `setWidth("Xpx")` **und** `setMaxWidth("100%")`
- [ ] Inline-Styles nur für dynamische Werte verwenden, alles andere per CSS-Klassen