PYDANTIC max_length (38 Routen, ~400 Field-Constraints): Schützt vor DoS durch Riesen-Payloads (10MB Thread-Titel etc.). Pragmatische Limits: - Titel/Name: 200 · Beschreibung/Body: 10000 · Notiz: 5000 - Email: 254 (RFC 5321) · URL: 500 · Slug/Kategorie: 100 - Hund-Name/Rasse: 80 · Hund-Bio: 2000 Top-betroffen: forum.py, diary.py, health.py, dogs.py, expenses.py, notes.py, auth.py, profile.py. Manuelle len()-Checks in profile, chat, ki entfernt (jetzt durch Field abgedeckt). PYTEST COVERAGE (+19 Tests, 37 grün + 1 xfail): - test_security.py: require_owner (Places GET/PATCH/DELETE mit Fremduser → 403), JWT-Blacklist (Logout invalidiert Token), Login-Lockout (5 Fehlversuche → 429 + Retry-After Header) - test_race.py: Invoice-Counter (20 parallele Threads, alle unique), Founder-Number (atomare Vergabe, voll bei 100) - test_validation.py: Forum-Titel 30k Zeichen → 422, Diary-Text 50k → 422 (verifiziert Pydantic max_length-Sweep) A11Y (Tap-Targets ≥44×44 + Dark-Mode-Kontrast): - #header-user-btn 36→44px, .header-back 40→44, .header-menu-btn 40→44 - dog-profile Wrapped-Slider Prev/Next 40→44 - forum-Lightbox Close 40→44 - --c-text-muted Light: #B0A090 (2.37:1 FAIL) → #7F6B58 (4.74:1 PASS) - --c-text-muted Dark: #806A58 (3.58:1 FAIL) → #A08878 (5.46:1 PASS) - Branding-Farben unangetastet
335 lines
11 KiB
CSS
335 lines
11 KiB
CSS
/* ============================================================
|
|
BAN YARO — Design System
|
|
Farben abgeleitet aus dem Foto von Ban Yaro (Parson Russell)
|
|
Mobile-first, CSS Custom Properties
|
|
============================================================ */
|
|
|
|
/* ------------------------------------------------------------
|
|
1. TOKENS — Farben, Abstände, Typografie, Schatten
|
|
------------------------------------------------------------ */
|
|
:root {
|
|
color-scheme: dark light;
|
|
/* Primärfarben — Honig-Amber aus Ban Yaros Fell */
|
|
--c-primary: #C4843A;
|
|
--c-primary-dark: #9E6520;
|
|
--c-primary-light: #E8C48A;
|
|
--c-primary-subtle: #FAF0E0;
|
|
|
|
/* Oberflächen — Warmweiß und Strohbeige */
|
|
--c-bg: #FAF7F2;
|
|
--c-bg-secondary: #F2EDE4; /* Karten auf Seitenhintergrund */
|
|
--c-surface: #FFFFFF;
|
|
--c-surface-2: #EDE5D4;
|
|
--c-surface-3: #DDD0BB;
|
|
--c-border: #E0D6C8;
|
|
--c-border-light: #EDE8E0;
|
|
|
|
/* Natur — Grün des Feldes, Blaugrau des Himmels */
|
|
--c-nature: #6B8055;
|
|
--c-nature-light: #A0B885;
|
|
--c-nature-subtle: #EDF2E8;
|
|
--c-sky: #8FAAB8;
|
|
--c-sky-subtle: #EAF1F5;
|
|
|
|
/* Text — Warmbraun aus dem Halsband */
|
|
--c-text: #2A1F14;
|
|
--c-text-secondary: #7A6A58;
|
|
--c-text-muted: #7F6B58; /* a11y: WCAG AA 4.74:1 auf --c-bg #FAF7F2 (vorher #B0A090 = 2.37:1) */
|
|
--c-text-inverse: #FAF7F2;
|
|
|
|
/* Funktionsfarben */
|
|
--c-danger: #C4391A;
|
|
--c-danger-dark: #9E2A10;
|
|
--c-danger-subtle: #FDEEE9;
|
|
--c-danger-border: rgba(196,57,26,0.3);
|
|
--c-success: #5B8A4A;
|
|
--c-success-subtle: #EBF4E7;
|
|
--c-warning: #D4923A;
|
|
--c-warning-subtle: #FDF3E3;
|
|
--c-amber: #E4A020; /* Goldgelb — "Heute"-Akzent, distinct von Primary */
|
|
--c-icon: #7A6A58; /* Standard-Icon-Farbe (= text-secondary im Light-Mode) */
|
|
--c-info: #4A7A9B;
|
|
--c-info-subtle: #E8F2F8;
|
|
|
|
/* Primär-Akzentfläche (für Hover-Hintergründe über Primary) */
|
|
--c-primary-soft: #FDF0E3;
|
|
|
|
/* Typografie */
|
|
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
--font-mono: "SF Mono", "Fira Code", Consolas, monospace;
|
|
|
|
--text-xs: 0.75rem; /* 12px */
|
|
--text-sm: 0.875rem; /* 14px */
|
|
--text-base: 1rem; /* 16px */
|
|
--text-md: 1.125rem; /* 18px */
|
|
--text-lg: 1.25rem; /* 20px */
|
|
--text-xl: 1.5rem; /* 24px */
|
|
--text-2xl: 1.875rem; /* 30px */
|
|
--text-3xl: 2.25rem; /* 36px */
|
|
|
|
--weight-normal: 400;
|
|
--weight-medium: 500;
|
|
--weight-semibold: 600;
|
|
--weight-bold: 700;
|
|
|
|
--leading-tight: 1.25;
|
|
--leading-normal: 1.5;
|
|
--leading-relaxed:1.75;
|
|
|
|
/* Abstände (4px-Raster) */
|
|
--space-1: 0.25rem; /* 4px */
|
|
--space-2: 0.5rem; /* 8px */
|
|
--space-3: 0.75rem; /* 12px */
|
|
--space-4: 1rem; /* 16px */
|
|
--space-5: 1.25rem; /* 20px */
|
|
--space-6: 1.5rem; /* 24px */
|
|
--space-8: 2rem; /* 32px */
|
|
--space-10: 2.5rem; /* 40px */
|
|
--space-12: 3rem; /* 48px */
|
|
--space-16: 4rem; /* 64px */
|
|
|
|
/* Radien */
|
|
--radius-sm: 0.375rem; /* 6px */
|
|
--radius-md: 0.625rem; /* 10px */
|
|
--radius-lg: 1rem; /* 16px */
|
|
--radius-xl: 1.5rem; /* 24px */
|
|
--radius-full: 9999px;
|
|
|
|
/* Schatten */
|
|
--shadow-xs: 0 1px 2px rgba(42, 31, 20, 0.06);
|
|
--shadow-sm: 0 1px 4px rgba(42, 31, 20, 0.08), 0 1px 2px rgba(42, 31, 20, 0.05);
|
|
--shadow-md: 0 4px 12px rgba(42, 31, 20, 0.10), 0 2px 4px rgba(42, 31, 20, 0.06);
|
|
--shadow-lg: 0 8px 24px rgba(42, 31, 20, 0.12), 0 4px 8px rgba(42, 31, 20, 0.06);
|
|
--shadow-xl: 0 16px 40px rgba(42, 31, 20, 0.14), 0 8px 16px rgba(42, 31, 20, 0.08);
|
|
|
|
/* Übergänge */
|
|
--transition-fast: 120ms ease;
|
|
--transition-normal: 200ms ease;
|
|
--transition-slow: 320ms ease;
|
|
|
|
/* Navigation */
|
|
--nav-bottom-height: 78px; /* Welten-Zurück-FAB: 54px + 20px bottom + 4px Abstand */
|
|
--nav-sidebar-width: 240px;
|
|
--header-height: 0px; /* Header entfernt — Welten-Navigation übernimmt */
|
|
|
|
/* Safe Areas (iPhone Notch/Home Indicator) */
|
|
--safe-top: env(safe-area-inset-top, 0px);
|
|
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
--safe-left: env(safe-area-inset-left, 0px);
|
|
--safe-right: env(safe-area-inset-right, 0px);
|
|
}
|
|
|
|
/* Dark Mode — System-Präferenz (nur wenn kein manuelles Theme gesetzt) */
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]):not([data-theme="dark"]) {
|
|
--c-bg: #1A1410;
|
|
--c-bg-secondary: #221A12; /* Karten auf Seitenhintergrund (dunkler) */
|
|
--c-surface: #241C14;
|
|
--c-surface-2: #2E2418;
|
|
--c-surface-3: #3A2E20;
|
|
--c-border: #4A3C2C;
|
|
--c-border-light: #3A2E20;
|
|
|
|
--c-primary-subtle: #2A1C0A;
|
|
--c-primary-soft: #2E1E08;
|
|
|
|
--c-nature-subtle: #1A2214;
|
|
--c-sky-subtle: #141C22;
|
|
--c-danger-subtle: #2A100A;
|
|
--c-danger-border: rgba(196,57,26,0.4);
|
|
--c-success-subtle: #122010;
|
|
--c-warning-subtle: #261A08;
|
|
--c-info-subtle: #10182A;
|
|
|
|
--c-text: #F0EAE0;
|
|
--c-text-secondary: #C0B0A0;
|
|
--c-text-muted: #9A8878;
|
|
--c-text-inverse: #2A1F14;
|
|
--c-icon: #B0A090;
|
|
--c-amber: #C48820;
|
|
--c-success: #6A9E58;
|
|
|
|
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.30);
|
|
--shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.40), 0 2px 4px rgba(0, 0, 0, 0.25);
|
|
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.45), 0 4px 8px rgba(0, 0, 0, 0.30);
|
|
--shadow-xl: 0 16px 40px rgba(0, 0, 0, 0.50), 0 8px 16px rgba(0, 0, 0, 0.35);
|
|
}
|
|
}
|
|
|
|
/* Manuelles Dark-Theme via data-theme="dark" (überschreibt auch prefers-color-scheme: light) */
|
|
:root[data-theme="dark"] {
|
|
--c-bg: #1A1410;
|
|
--c-bg-secondary: #221A12;
|
|
--c-surface: #241C14;
|
|
--c-surface-2: #2E2418;
|
|
--c-surface-3: #3A2E20;
|
|
--c-border: #4A3C2C;
|
|
--c-border-light: #3A2E20;
|
|
|
|
--c-primary-subtle: #2A1C0A;
|
|
--c-primary-soft: #2E1E08;
|
|
|
|
--c-nature-subtle: #1A2214;
|
|
--c-sky-subtle: #141C22;
|
|
--c-danger-subtle: #2A100A;
|
|
--c-success-subtle: #122010;
|
|
--c-warning-subtle: #261A08;
|
|
--c-info-subtle: #10182A;
|
|
|
|
--c-text: #F0EAE0;
|
|
--c-text-secondary: #C0B0A0;
|
|
--c-text-muted: #A08878; /* a11y: WCAG AA 5.46:1 auf --c-bg #1A1410 (vorher #806A58 = 3.58:1) */
|
|
--c-text-inverse: #2A1F14;
|
|
|
|
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.30);
|
|
--shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.40), 0 2px 4px rgba(0, 0, 0, 0.25);
|
|
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.45), 0 4px 8px rgba(0, 0, 0, 0.30);
|
|
--shadow-xl: 0 16px 40px rgba(0, 0, 0, 0.50), 0 8px 16px rgba(0, 0, 0, 0.35);
|
|
}
|
|
|
|
/* Global Dark-Mode für alle Leaflet-Karten (map, walks, lost, poison, forum, routes …) */
|
|
:root[data-theme="dark"] .leaflet-tile-pane {
|
|
filter: invert(93%) hue-rotate(180deg) brightness(0.88) contrast(0.88) saturate(0.85);
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) .leaflet-tile-pane {
|
|
filter: invert(93%) hue-rotate(180deg) brightness(0.88) contrast(0.88) saturate(0.85);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
2. RESET & BASE
|
|
------------------------------------------------------------ */
|
|
*, *::before, *::after {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
html {
|
|
font-size: 16px;
|
|
-webkit-text-size-adjust: 100%;
|
|
scroll-behavior: smooth;
|
|
height: 100%;
|
|
}
|
|
|
|
body {
|
|
font-family: var(--font-sans);
|
|
font-size: var(--text-base);
|
|
font-weight: var(--weight-normal);
|
|
line-height: var(--leading-normal);
|
|
color: var(--c-text);
|
|
background-color: var(--c-bg);
|
|
min-height: 100%;
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
}
|
|
|
|
img, video, svg {
|
|
display: block;
|
|
max-width: 100%;
|
|
}
|
|
|
|
a {
|
|
color: var(--c-primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
a:hover {
|
|
color: var(--c-primary-dark);
|
|
}
|
|
|
|
button {
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
border: none;
|
|
background: none;
|
|
}
|
|
|
|
input, textarea, select {
|
|
font-family: inherit;
|
|
font-size: inherit;
|
|
}
|
|
|
|
ul, ol {
|
|
list-style: none;
|
|
}
|
|
|
|
/* ------------------------------------------------------------
|
|
3. TYPOGRAFIE-HELFER
|
|
------------------------------------------------------------ */
|
|
.text-xs { font-size: var(--text-xs); }
|
|
.text-sm { font-size: var(--text-sm); }
|
|
.text-base { font-size: var(--text-base); }
|
|
.text-md { font-size: var(--text-md); }
|
|
.text-lg { font-size: var(--text-lg); }
|
|
.text-xl { font-size: var(--text-xl); }
|
|
.text-2xl { font-size: var(--text-2xl); }
|
|
|
|
.font-medium { font-weight: var(--weight-medium); }
|
|
.font-semibold{ font-weight: var(--weight-semibold); }
|
|
.font-bold { font-weight: var(--weight-bold); }
|
|
|
|
.text-primary { color: var(--c-primary); }
|
|
.text-secondary { color: var(--c-text-secondary); }
|
|
.text-muted { color: var(--c-text-muted); }
|
|
.text-danger { color: var(--c-danger); }
|
|
.text-success { color: var(--c-success); }
|
|
.text-nature { color: var(--c-nature); }
|
|
|
|
.text-center { text-align: center; }
|
|
.text-right { text-align: right; }
|
|
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
|
|
/* ------------------------------------------------------------
|
|
4. LAYOUT-HELFER
|
|
------------------------------------------------------------ */
|
|
.flex { display: flex; }
|
|
.flex-col { display: flex; flex-direction: column; }
|
|
.items-center { align-items: center; }
|
|
.items-start { align-items: flex-start; }
|
|
.justify-between { justify-content: space-between; }
|
|
.justify-center { justify-content: center; }
|
|
.gap-1 { gap: var(--space-1); }
|
|
.gap-2 { gap: var(--space-2); }
|
|
.gap-3 { gap: var(--space-3); }
|
|
.gap-4 { gap: var(--space-4); }
|
|
.gap-6 { gap: var(--space-6); }
|
|
.flex-1 { flex: 1; }
|
|
.w-full { width: 100%; }
|
|
|
|
/* ------------------------------------------------------------
|
|
5. ABSTANDS-HELFER
|
|
------------------------------------------------------------ */
|
|
.p-2 { padding: var(--space-2); }
|
|
.p-3 { padding: var(--space-3); }
|
|
.p-4 { padding: var(--space-4); }
|
|
.p-6 { padding: var(--space-6); }
|
|
.px-4 { padding-left: var(--space-4); padding-right: var(--space-4); }
|
|
.py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); }
|
|
.py-3 { padding-top: var(--space-3); padding-bottom: var(--space-3); }
|
|
.mt-2 { margin-top: var(--space-2); }
|
|
.mt-4 { margin-top: var(--space-4); }
|
|
.mt-6 { margin-top: var(--space-6); }
|
|
.mb-2 { margin-bottom: var(--space-2); }
|
|
.mb-4 { margin-bottom: var(--space-4); }
|
|
.ml-auto { margin-left: auto; }
|
|
|
|
/* ------------------------------------------------------------
|
|
6. SICHTBARKEIT
|
|
------------------------------------------------------------ */
|
|
.hidden { display: none !important; }
|
|
|
|
/* Nur auf Mobile sichtbar */
|
|
.mobile-only { display: block; }
|
|
@media (min-width: 768px) {
|
|
.mobile-only { display: none !important; }
|
|
}
|
|
|
|
/* Nur auf Desktop sichtbar */
|
|
.desktop-only { display: none !important; }
|
|
@media (min-width: 768px) {
|
|
.desktop-only { display: block !important; }
|
|
}
|