diff --git a/public/pages/dashboard.js b/public/pages/dashboard.js index 9df89e3..3e1d1d5 100644 --- a/public/pages/dashboard.js +++ b/public/pages/dashboard.js @@ -85,7 +85,7 @@ function widgetHeader(icon, title, count, linkHref, linkLabel = 'Alle') { ${badge} - ${linkLabel} + ${linkLabel} `; @@ -97,13 +97,11 @@ function widgetHeader(icon, title, count, linkHref, linkLabel = 'Alle') { function skeletonWidget(lines = 3) { const lineHtml = Array.from({ length: lines }, (_, i) => ` -
+ `).join(''); return ` `; @@ -116,20 +114,21 @@ function skeletonWidget(lines = 3) { function renderGreeting(user, stats = {}) { const { urgentCount = 0, todayEventCount = 0, todayMealTitle = null } = stats; + const chipIcon = 'width:12px;height:12px;flex-shrink:0;'; const statChips = []; if (urgentCount > 0) statChips.push(` - + ${urgentCount} dring. Aufgabe${urgentCount > 1 ? 'n' : ''} `); if (todayEventCount > 0) statChips.push(` - + ${todayEventCount} Termin${todayEventCount > 1 ? 'e' : ''} heute `); if (todayMealTitle) statChips.push(` - + Heute: ${todayMealTitle} `); @@ -149,7 +148,7 @@ function renderUrgentTasks(tasks) { return ``; @@ -182,7 +181,7 @@ function renderUpcomingEvents(events) { return ``; @@ -239,7 +238,7 @@ function renderPinnedNotes(notes) { return ``; diff --git a/public/router.js b/public/router.js index af17a65..1480445 100644 --- a/public/router.js +++ b/public/router.js @@ -116,12 +116,20 @@ async function renderPage(route) { renderAppShell(app); } + const content = document.getElementById('page-content') || app; + + // Alte Seite kurz ausfaden, falls vorhanden + const oldPage = content.querySelector('.page-transition'); + if (oldPage) { + oldPage.classList.add('page-transition--out'); + await new Promise(r => setTimeout(r, 120)); + } + // Seiten-Wrapper bereits jetzt in den DOM einfügen, damit // document.getElementById() in render() die richtigen Elemente findet. const pageWrapper = document.createElement('div'); pageWrapper.className = 'page-transition'; pageWrapper.style.animation = 'page-in 0.2s ease forwards'; - const content = document.getElementById('page-content') || app; content.replaceChildren(pageWrapper); await module.render(pageWrapper, { user: currentUser }); @@ -236,7 +244,10 @@ function showToast(message, type = 'default', duration = 3000) { toast.setAttribute('role', 'alert'); container.appendChild(toast); - setTimeout(() => toast.remove(), duration); + setTimeout(() => { + toast.classList.add('toast--out'); + toast.addEventListener('animationend', () => toast.remove(), { once: true }); + }, duration); } // -------------------------------------------------------- diff --git a/public/styles/budget.css b/public/styles/budget.css index d94c87c..061ae67 100644 --- a/public/styles/budget.css +++ b/public/styles/budget.css @@ -56,12 +56,19 @@ * -------------------------------------------------------- */ .budget-summary { display: grid; - grid-template-columns: repeat(3, 1fr); - gap: var(--space-3); - padding: var(--space-4); + grid-template-columns: 1fr; + gap: var(--space-2); + padding: var(--space-3) var(--space-4); flex-shrink: 0; } +@media (min-width: 480px) { + .budget-summary { + grid-template-columns: repeat(3, 1fr); + gap: var(--space-3); + } +} + .budget-summary-card { background-color: var(--color-surface); border-radius: var(--radius-md); @@ -191,8 +198,8 @@ display: flex; align-items: center; gap: var(--space-3); - padding: var(--space-3) var(--space-4); - border-bottom: 1px solid var(--color-border); + padding: var(--space-2) var(--space-4); + border-bottom: 1px solid var(--color-border-subtle); cursor: pointer; transition: background-color var(--transition-fast); } @@ -283,7 +290,7 @@ .budget-modal-overlay { position: fixed; inset: 0; - background-color: rgba(0,0,0,0.5); + background-color: var(--color-overlay); z-index: var(--z-modal); display: flex; align-items: flex-end; @@ -366,7 +373,7 @@ .amount-type-btn { padding: var(--space-2) var(--space-3); - border-radius: 6px; + border-radius: var(--radius-xs); border: none; font-size: var(--text-sm); font-weight: var(--font-weight-medium); diff --git a/public/styles/calendar.css b/public/styles/calendar.css index 6077b07..cb0e99e 100644 --- a/public/styles/calendar.css +++ b/public/styles/calendar.css @@ -73,7 +73,7 @@ font-size: var(--text-xs); font-weight: var(--font-weight-medium); padding: var(--space-1) var(--space-2); - border-radius: 6px; + border-radius: var(--radius-xs); border: none; background: none; color: var(--color-text-secondary); @@ -83,6 +83,10 @@ white-space: nowrap; } +.cal-toolbar__view-btn:hover:not(.cal-toolbar__view-btn--active) { + color: var(--color-text-primary); +} + .cal-toolbar__view-btn--active { background-color: var(--color-surface); color: var(--color-text-primary); @@ -125,8 +129,8 @@ } .month-day { - border-right: 1px solid var(--color-border); - border-bottom: 1px solid var(--color-border); + border-right: 1px solid var(--color-border-subtle); + border-bottom: 1px solid var(--color-border-subtle); padding: var(--space-1); min-height: 80px; cursor: pointer; @@ -171,10 +175,10 @@ } .month-day__event { - font-size: 11px; + font-size: var(--text-xs); font-weight: var(--font-weight-medium); padding: 1px 5px; - border-radius: 3px; + border-radius: var(--radius-xs); margin-bottom: 2px; white-space: nowrap; overflow: hidden; @@ -185,7 +189,7 @@ } .month-day__more { - font-size: 11px; + font-size: var(--text-xs); color: var(--color-text-secondary); padding: 1px 4px; cursor: pointer; @@ -215,7 +219,7 @@ .week-view__day-header { padding: var(--space-2) var(--space-1); text-align: center; - border-left: 1px solid var(--color-border); + border-left: 1px solid var(--color-border-subtle); } .week-view__day-name { @@ -273,7 +277,7 @@ } .week-view__time-label { - font-size: 10px; + font-size: var(--text-xs); color: var(--color-text-disabled); white-space: nowrap; } @@ -285,7 +289,7 @@ } .week-view__col { - border-left: 1px solid var(--color-border); + border-left: 1px solid var(--color-border-subtle); position: relative; } @@ -294,7 +298,7 @@ left: 0; right: 0; height: 1px; - background-color: var(--color-border); + background-color: var(--color-border-subtle); pointer-events: none; } @@ -323,9 +327,9 @@ position: absolute; left: 2px; right: 2px; - border-radius: 4px; + border-radius: var(--radius-xs); padding: 2px 5px; - font-size: 11px; + font-size: var(--text-xs); font-weight: var(--font-weight-medium); color: #ffffff; overflow: hidden; @@ -346,7 +350,7 @@ } .week-event__time { - font-size: 10px; + font-size: var(--text-xs); opacity: 0.85; } @@ -391,7 +395,7 @@ .day-view__col { flex: 1; - border-left: 1px solid var(--color-border); + border-left: 1px solid var(--color-border-subtle); position: relative; } @@ -514,7 +518,7 @@ .event-modal-overlay { position: fixed; inset: 0; - background-color: rgba(0, 0, 0, 0.5); + background-color: var(--color-overlay); z-index: var(--z-modal); display: flex; align-items: flex-end; @@ -700,15 +704,15 @@ } .allday-cell { - border-left: 1px solid var(--color-border); + border-left: 1px solid var(--color-border-subtle); padding: 2px; } .allday-event { - font-size: 11px; + font-size: var(--text-xs); font-weight: var(--font-weight-medium); padding: 1px 6px; - border-radius: 3px; + border-radius: var(--radius-xs); color: #ffffff; white-space: nowrap; overflow: hidden; diff --git a/public/styles/contacts.css b/public/styles/contacts.css index 19aeb8c..a8ee17d 100644 --- a/public/styles/contacts.css +++ b/public/styles/contacts.css @@ -98,6 +98,11 @@ min-height: 30px; } +.contact-filter-chip:hover:not(.contact-filter-chip--active) { + border-color: var(--color-text-secondary); + color: var(--color-text-primary); +} + .contact-filter-chip--active { background-color: var(--color-accent); border-color: var(--color-accent); @@ -136,7 +141,7 @@ align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); - border-bottom: 1px solid var(--color-border); + border-bottom: 1px solid var(--color-border-subtle); cursor: pointer; transition: background-color var(--transition-fast); } @@ -231,7 +236,7 @@ .contact-modal-overlay { position: fixed; inset: 0; - background-color: rgba(0,0,0,0.5); + background-color: var(--color-overlay); z-index: var(--z-modal); display: flex; align-items: flex-end; diff --git a/public/styles/dashboard.css b/public/styles/dashboard.css index 10fd413..0484b85 100644 --- a/public/styles/dashboard.css +++ b/public/styles/dashboard.css @@ -16,13 +16,24 @@ @media (min-width: 1024px) { .dashboard { - padding: var(--space-8); - padding-bottom: var(--space-16); + padding: var(--space-6) var(--space-8); + padding-bottom: var(--space-12); + } +} + +@media (min-width: 1440px) { + .dashboard { + padding: var(--space-6) var(--space-10); } } /* -------------------------------------------------------- * Widget-Grid + * + * Mobile: 1 Spalte + * Tablet: 2 Spalten + * Desktop: 3 Spalten + * Wide: 4 Spalten (ab 1440px) * -------------------------------------------------------- */ .dashboard__grid { display: grid; @@ -43,6 +54,7 @@ @media (min-width: 1024px) { .dashboard__grid { grid-template-columns: repeat(3, 1fr); + gap: var(--space-5); } .widget--wide { @@ -50,13 +62,29 @@ } } +@media (min-width: 1440px) { + .dashboard__grid { + grid-template-columns: repeat(4, 1fr); + } + + /* Wide-Widgets nehmen 2 von 4 Spalten ein */ + .widget--wide { + grid-column: span 2; + } + + /* Greeting-Widget über alle 4 Spalten */ + .widget-greeting { + grid-column: 1 / -1; + } +} + /* -------------------------------------------------------- * Begrüßungs-Widget * -------------------------------------------------------- */ .widget-greeting { - background: linear-gradient(135deg, var(--color-accent) 0%, #5B9FFF 100%); + background: linear-gradient(135deg, var(--color-accent) 0%, #6BA3FF 100%); border-radius: var(--radius-md); - padding: var(--space-5) var(--space-6); + padding: var(--space-4) var(--space-5); color: #ffffff; grid-column: 1 / -1; } @@ -64,16 +92,22 @@ .widget-greeting__content { display: flex; flex-direction: column; - gap: var(--space-1); + gap: 2px; } .widget-greeting__title { - font-size: var(--text-2xl); + font-size: var(--text-xl); font-weight: var(--font-weight-bold); } +@media (min-width: 1024px) { + .widget-greeting__title { + font-size: var(--text-2xl); + } +} + .widget-greeting__date { - font-size: var(--text-base); + font-size: var(--text-sm); opacity: 0.85; } @@ -81,20 +115,19 @@ display: flex; flex-wrap: wrap; gap: var(--space-2); - margin-top: var(--space-3); + margin-top: var(--space-2); } .greeting-chip { display: inline-flex; align-items: center; gap: 4px; - background: rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.18); color: #ffffff; font-size: var(--text-xs); font-weight: var(--font-weight-medium); - padding: 3px var(--space-2); + padding: 2px var(--space-2); border-radius: var(--radius-full); - backdrop-filter: blur(4px); } .greeting-chip--warn { @@ -103,6 +136,9 @@ /* -------------------------------------------------------- * Basis-Widget (Card) + * + * Kompaktes Padding: 12px Header, 12px Body. + * Internes Spacing: 8–12px (enger als vorher 16–24px). * -------------------------------------------------------- */ .widget { background-color: var(--color-surface); @@ -117,12 +153,11 @@ display: flex; align-items: center; justify-content: space-between; - padding: var(--space-4) var(--space-4) var(--space-3); - border-bottom: 1px solid var(--color-border); + padding: var(--space-3) var(--space-4) var(--space-2); } .widget__title { - font-size: var(--text-base); + font-size: var(--text-sm); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); display: flex; @@ -131,18 +166,23 @@ } .widget__title-icon { - width: 18px; - height: 18px; + width: 16px; + height: 16px; color: var(--color-accent); } .widget__link { - font-size: var(--text-sm); + font-size: var(--text-xs); color: var(--color-accent); font-weight: var(--font-weight-medium); display: inline-flex; align-items: center; gap: 2px; + transition: opacity var(--transition-fast); +} + +.widget__link:hover { + opacity: 0.7; } .widget__badge { @@ -151,10 +191,10 @@ justify-content: center; background-color: var(--color-accent); color: #ffffff; - font-size: 10px; + font-size: var(--text-2xs); font-weight: var(--font-weight-bold); - min-width: 18px; - height: 18px; + min-width: 16px; + height: 16px; padding: 0 4px; border-radius: var(--radius-full); line-height: 1; @@ -162,40 +202,48 @@ .widget__body { flex: 1; - padding: var(--space-3) var(--space-4); + padding: var(--space-2) var(--space-4) var(--space-3); } .widget__empty { - padding: var(--space-6) var(--space-4); + padding: var(--space-5) var(--space-4); text-align: center; - color: var(--color-text-secondary); + color: var(--color-text-tertiary); font-size: var(--text-sm); display: flex; flex-direction: column; align-items: center; - gap: var(--space-2); + gap: var(--space-1); } -/* Widget hover lift (desktop) */ +.widget__empty .empty-state__icon { + width: 28px; + height: 28px; + color: var(--color-text-disabled); + margin-bottom: var(--space-1); +} + +/* Widget hover lift (desktop) — dezent, max 1px */ @media (min-width: 1024px) { .widget { transition: transform var(--transition-fast), box-shadow var(--transition-fast); } .widget:hover { - transform: translateY(-2px); + transform: translateY(-1px); box-shadow: var(--shadow-md); } } /* -------------------------------------------------------- * Aufgaben-Widget + * Kompakte Items: 8px vertikales Padding, enger Spacing. * -------------------------------------------------------- */ .task-item { display: flex; align-items: flex-start; - gap: var(--space-3); - padding: var(--space-3) 0; - border-bottom: 1px solid var(--color-border); + gap: var(--space-2); + padding: var(--space-2) 0; + border-bottom: 1px solid var(--color-border-subtle); cursor: pointer; transition: opacity var(--transition-fast); } @@ -205,19 +253,21 @@ } .task-item:hover { - opacity: 0.75; + opacity: 0.7; } .task-item__priority { - width: 8px; - height: 8px; + width: 7px; + height: 7px; border-radius: var(--radius-full); flex-shrink: 0; - margin-top: 6px; + margin-top: 5px; } .task-item__priority--urgent { background-color: var(--color-priority-urgent); } .task-item__priority--high { background-color: var(--color-priority-high); } +.task-item__priority--medium { background-color: var(--color-priority-medium); } +.task-item__priority--low { background-color: var(--color-priority-low); } .task-item__content { flex: 1; @@ -231,12 +281,13 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + line-height: var(--line-height-snug); } .task-item__meta { font-size: var(--text-xs); - color: var(--color-text-secondary); - margin-top: 2px; + color: var(--color-text-tertiary); + margin-top: 1px; } .task-item__meta--overdue { @@ -245,27 +296,29 @@ } .task-item__avatar { - width: 24px; - height: 24px; + width: 22px; + height: 22px; border-radius: var(--radius-full); display: flex; align-items: center; justify-content: center; - font-size: 10px; + font-size: 9px; font-weight: var(--font-weight-bold); color: #ffffff; flex-shrink: 0; + margin-top: 1px; } /* -------------------------------------------------------- * Termine-Widget + * Kompakt: Farbbalken + Inhalt, enge Abstände. * -------------------------------------------------------- */ .event-item { display: flex; align-items: stretch; - gap: var(--space-3); - padding: var(--space-3) 0; - border-bottom: 1px solid var(--color-border); + gap: var(--space-2); + padding: var(--space-2) 0; + border-bottom: 1px solid var(--color-border-subtle); cursor: pointer; transition: opacity var(--transition-fast); } @@ -275,7 +328,7 @@ } .event-item:hover { - opacity: 0.75; + opacity: 0.7; } .event-item__bar { @@ -297,23 +350,24 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + line-height: var(--line-height-snug); } .event-item__time { font-size: var(--text-xs); - color: var(--color-text-secondary); - margin-top: 2px; + color: var(--color-text-tertiary); + margin-top: 1px; display: flex; align-items: center; gap: var(--space-1); } .event-time-badge { - font-size: 10px; + font-size: var(--text-2xs); font-weight: var(--font-weight-semibold); padding: 1px 5px; border-radius: var(--radius-full); - background-color: var(--color-surface-2); + background-color: var(--color-surface-3); color: var(--color-text-secondary); } @@ -341,13 +395,13 @@ flex-direction: column; align-items: center; justify-content: center; - gap: var(--space-1); - padding: var(--space-3) var(--space-2); + gap: 2px; + padding: var(--space-2) var(--space-1); background-color: var(--color-surface); cursor: pointer; transition: background-color var(--transition-fast); text-align: center; - min-height: 88px; + min-height: 72px; } .meal-slot:hover { @@ -363,8 +417,8 @@ } .meal-slot__icon { - width: 20px; - height: 20px; + width: 18px; + height: 18px; color: var(--color-text-disabled); flex-shrink: 0; } @@ -374,7 +428,7 @@ } .meal-slot__type { - font-size: 10px; + font-size: var(--text-2xs); font-weight: var(--font-weight-semibold); color: var(--color-text-disabled); text-transform: uppercase; @@ -430,50 +484,6 @@ transform: translateY(-1px); } -/* legacy (old .note-item had margin-bottom, now grid handles gap) */ - -/* -------------------------------------------------------- - * Alte Essen-Listen-Styles (Fallback, nicht mehr primär) - * -------------------------------------------------------- */ -.meal-item { - display: flex; - align-items: center; - gap: var(--space-3); - padding: var(--space-3) 0; - border-bottom: 1px solid var(--color-border); - cursor: pointer; - transition: opacity var(--transition-fast); -} - -.meal-item:last-child { - border-bottom: none; -} - -.meal-item:hover { - opacity: 0.75; -} - -.meal-item__type-badge { - font-size: var(--text-xs); - font-weight: var(--font-weight-semibold); - padding: 2px var(--space-2); - border-radius: var(--radius-xs); - background-color: var(--color-accent-light); - color: var(--color-accent); - white-space: nowrap; - flex-shrink: 0; - min-width: 72px; - text-align: center; -} - -.meal-item__title { - font-size: var(--text-sm); - color: var(--color-text-primary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - .note-item__title { font-size: var(--text-sm); font-weight: var(--font-weight-semibold); @@ -495,9 +505,12 @@ /* -------------------------------------------------------- * Wetter-Widget + * + * Eigener Gradient, überschreibt .widget Hintergrund. + * Kompakt: Icon kleiner, Forecast enger. * -------------------------------------------------------- */ .weather-widget { - background: linear-gradient(135deg, #1a73e8 0%, #0f4fa8 100%); + background: linear-gradient(135deg, var(--color-accent) 0%, #1E5CB3 100%); color: #ffffff; } @@ -505,47 +518,47 @@ display: flex; align-items: center; justify-content: space-between; - padding: var(--space-4) var(--space-5); + padding: var(--space-3) var(--space-4); } .weather-widget__temp { - font-size: var(--text-4xl, 2.25rem); + font-size: var(--text-4xl); font-weight: var(--font-weight-bold); line-height: 1; - margin-bottom: var(--space-1); -} - -.weather-widget__desc { - font-size: var(--text-base); - font-weight: var(--font-weight-medium); - text-transform: capitalize; margin-bottom: 2px; } -.weather-widget__city { +.weather-widget__desc { font-size: var(--text-sm); + font-weight: var(--font-weight-medium); + text-transform: capitalize; + margin-bottom: 1px; +} + +.weather-widget__city { + font-size: var(--text-xs); opacity: 0.85; - margin-bottom: var(--space-2); + margin-bottom: var(--space-1); } .weather-widget__meta { font-size: var(--text-xs); - opacity: 0.75; - line-height: 1.4; + opacity: 0.7; + line-height: 1.3; } .weather-widget__icon { - width: 80px; - height: 80px; + width: 64px; + height: 64px; flex-shrink: 0; - filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)); + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15)); } .weather-forecast { display: flex; - border-top: 1px solid rgba(255,255,255,0.2); - padding: var(--space-3) var(--space-5); - gap: var(--space-4); + border-top: 1px solid rgba(255, 255, 255, 0.15); + padding: var(--space-2) var(--space-4); + gap: var(--space-3); } .weather-forecast__day { @@ -564,13 +577,13 @@ } .weather-forecast__icon { - width: 32px; - height: 32px; + width: 28px; + height: 28px; } .weather-forecast__temps { display: flex; - gap: var(--space-2); + gap: var(--space-1); font-size: var(--text-xs); } @@ -579,28 +592,29 @@ } .weather-forecast__low { - opacity: 0.65; + opacity: 0.6; } /* -------------------------------------------------------- * Skeleton-Zustände (pro Widget) + * Kompakt: gleiches Spacing wie echte Widgets. * -------------------------------------------------------- */ .widget-skeleton { background-color: var(--color-surface); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); - padding: var(--space-4); + padding: var(--space-3) var(--space-4); } .skeleton-line { - height: 14px; + height: 12px; margin-bottom: var(--space-2); border-radius: var(--radius-xs); } -.skeleton-line--short { width: 40%; } -.skeleton-line--medium { width: 65%; } -.skeleton-line--full { width: 100%; } +.skeleton-line--short { width: 35%; } +.skeleton-line--medium { width: 60%; } +.skeleton-line--full { width: 100%; } /* -------------------------------------------------------- * FAB Speed-Dial @@ -623,8 +637,8 @@ } .fab-main { - width: 56px; - height: 56px; + width: 52px; + height: 52px; border-radius: var(--radius-full); background-color: var(--color-accent); color: #ffffff; @@ -633,18 +647,31 @@ align-items: center; justify-content: center; cursor: pointer; - transition: transform var(--transition-base), background-color var(--transition-fast); + transition: + transform var(--transition-base), + background-color var(--transition-fast); border: none; flex-shrink: 0; } +@media (min-width: 1024px) { + .fab-main { + width: 48px; + height: 48px; + } +} + .fab-main:hover { background-color: var(--color-accent-hover); } +.fab-main:active { + transform: scale(0.95); +} + .fab-main--open { transform: rotate(45deg); - background-color: var(--color-text-secondary); + background-color: var(--neutral-600); } .fab-actions { @@ -683,8 +710,8 @@ } .fab-action__btn { - width: 44px; - height: 44px; + width: 40px; + height: 40px; border-radius: var(--radius-full); background-color: var(--color-surface); color: var(--color-accent); diff --git a/public/styles/layout.css b/public/styles/layout.css index d7582b9..bee41a4 100644 --- a/public/styles/layout.css +++ b/public/styles/layout.css @@ -2,6 +2,13 @@ * Modul: Layout * Zweck: App-Shell-Layout, Navigation (Bottom Mobile / Sidebar Desktop), Responsive Grid * Abhängigkeiten: tokens.css, reset.css + * + * Navigation: + * Mobile (<1024px): Bottom-Nav-Bar, 5 Haupt-Module + * Desktop (1024–1279px): Collapsed Sidebar, nur Icons (56px) + * Wide Desktop (≥1280px): Expanded Sidebar mit Labels (220px) + * + * Referenz: Apple HIG Sidebar-Pattern */ /* -------------------------------------------------------- @@ -36,6 +43,24 @@ 50% { opacity: 0.4; } } +/* -------------------------------------------------------- + * Seiten-Übergangs-Animation + * -------------------------------------------------------- */ +@keyframes page-in { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes page-out { + from { opacity: 1; } + to { opacity: 0; } +} + +.page-transition--out { + animation: page-out 0.12s ease forwards; + pointer-events: none; +} + /* -------------------------------------------------------- * Layout: Mobile (Standard, < 1024px) * -------------------------------------------------------- */ @@ -45,8 +70,16 @@ overflow-y: auto; } +/* Sidebar auf Mobile verstecken */ +.nav-sidebar { + display: none; +} + /* -------------------------------------------------------- * Bottom Navigation (Mobil + Tablet) + * + * Kompakte Bar: 56px Höhe, Icons 22px, Labels 11px. + * Leichter Blur-Effekt für Glassmorphismus. * -------------------------------------------------------- */ .nav-bottom { position: fixed; @@ -55,13 +88,13 @@ right: 0; height: calc(var(--nav-height-mobile) + var(--safe-area-inset-bottom)); padding-bottom: var(--safe-area-inset-bottom); - background-color: var(--color-surface); - border-top: 1px solid var(--color-border); + background-color: color-mix(in srgb, var(--color-surface) 85%, transparent); + border-top: 1px solid var(--color-border-subtle); display: flex; align-items: center; z-index: var(--z-nav); - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); + backdrop-filter: blur(16px) saturate(180%); + -webkit-backdrop-filter: blur(16px) saturate(180%); } .nav-bottom__items { @@ -70,37 +103,50 @@ height: 100%; } +/* ── Nav-Item (Bottom-Bar): Basis-State ── */ .nav-item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; - gap: var(--space-1); - padding: var(--space-2) var(--space-1); - color: var(--color-text-secondary); + gap: 2px; + padding: var(--space-1) var(--space-1); + color: var(--color-text-tertiary); transition: color var(--transition-fast); -webkit-tap-highlight-color: transparent; - min-height: unset; /* Reset des Touch-Target-Minimums für Nav-Items */ + min-height: unset; + text-decoration: none; } +.nav-item:active { + transform: scale(0.92); +} + +/* ── Nav-Item: Aktiv ── */ .nav-item[aria-current="page"] { color: var(--color-accent); } .nav-item__icon { - width: 24px; - height: 24px; + width: 22px; + height: 22px; + flex-shrink: 0; } .nav-item__label { - font-size: var(--text-xs); + font-size: var(--text-2xs); font-weight: var(--font-weight-medium); + line-height: 1; } -/* -------------------------------------------------------- - * Sidebar Navigation — Desktop (Neumorphismus) - * -------------------------------------------------------- */ +/* ================================================================ + * Sidebar Navigation — Desktop (≥ 1024px) + * + * Design: Flach, kein Neumorphismus. Dezenter Seitenrand. + * Aktiver State: Hintergrund-Highlight + Akzentstreifen links. + * Hover: Subtile Hintergrundfarbe, kein Shadow. + * ================================================================ */ @media (min-width: 1024px) { .app-shell { flex-direction: row; @@ -117,23 +163,19 @@ display: none; } - /* ── Sidebar Container ── */ + /* ── Sidebar anzeigen ── */ .nav-sidebar { + display: flex; position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-width); background: var(--sidebar-bg); - border-right: none; - box-shadow: - 6px 0 28px var(--sidebar-shadow-dark), - 2px 0 8px var(--sidebar-shadow-dark), - inset -1px 0 0 var(--sidebar-shadow-light); - display: flex; + border-right: 1px solid var(--color-border-subtle); flex-direction: column; z-index: var(--z-nav); - padding: var(--space-4) 0 var(--space-6); + padding: var(--space-4) 0 var(--space-4); transition: width var(--transition-slow); overflow: hidden; } @@ -143,18 +185,18 @@ display: flex; justify-content: center; align-items: center; - padding: var(--space-3) 0 var(--space-5); - margin-bottom: var(--space-2); + padding: var(--space-2) 0 var(--space-4); + margin-bottom: var(--space-1); flex-shrink: 0; } - /* Neumorphic App-Icon als Logo-Ersatz */ + /* App-Icon als kompaktes Logo */ .nav-sidebar__logo::before { content: 'O'; - width: 36px; - height: 36px; + width: 32px; + height: 32px; border-radius: var(--radius-sm); - background: linear-gradient(135deg, var(--color-accent) 0%, #5856D6 100%); + background: linear-gradient(135deg, var(--color-accent) 0%, #7C5CFC 100%); color: #ffffff; font-size: var(--text-sm); font-weight: var(--font-weight-bold); @@ -162,12 +204,9 @@ align-items: center; justify-content: center; flex-shrink: 0; - box-shadow: - 3px 3px 8px var(--sidebar-shadow-dark), - -2px -2px 6px var(--sidebar-shadow-light); } - /* Volltext-Span verstecken im collapsed-Modus */ + /* Logo-Text verstecken im collapsed-Modus */ .nav-sidebar__logo > span { display: none; } @@ -176,7 +215,7 @@ .nav-sidebar__items { display: flex; flex-direction: column; - gap: 4px; + gap: var(--space-0h); padding: 0 var(--space-2); flex: 1; overflow-y: auto; @@ -184,29 +223,27 @@ } .nav-sidebar__items::-webkit-scrollbar { display: none; } - /* ── Nav-Item: Basis ── */ + /* ── Nav-Item: Sidebar-Basis ── */ .nav-sidebar .nav-item { flex-direction: row; justify-content: center; align-items: center; - border-radius: var(--radius-xs); - padding: 6px var(--space-2); + border-radius: var(--radius-sm); + padding: 8px var(--space-2); gap: 0; min-height: unset; font-size: var(--text-sm); color: var(--color-text-secondary); background: transparent; - box-shadow: none; transition: - box-shadow var(--transition-base), color var(--transition-fast), - background var(--transition-fast); + background-color var(--transition-fast); position: relative; } .nav-sidebar .nav-item__icon { - width: 18px; - height: 18px; + width: 20px; + height: 20px; flex-shrink: 0; } @@ -218,47 +255,45 @@ overflow: hidden; } - /* Hover: sehr subtiler raised-Effekt */ + /* ── Hover: dezente Hintergrundfarbe ── */ .nav-sidebar .nav-item:hover:not([aria-current="page"]) { - color: var(--color-accent); - background: var(--sidebar-bg); - box-shadow: - 2px 2px 5px var(--sidebar-shadow-dark), - -2px -2px 5px var(--sidebar-shadow-light); + color: var(--color-text-primary); + background-color: var(--color-surface-3); } - /* Aktiv: leichter inset */ + /* ── Aktiv: Hintergrund-Highlight + Akzent-Indikator ── */ .nav-sidebar .nav-item[aria-current="page"] { color: var(--color-accent); font-weight: var(--font-weight-semibold); - background: var(--sidebar-bg); - box-shadow: - inset 1px 1px 3px var(--sidebar-shadow-dark), - inset -1px -1px 3px var(--sidebar-shadow-light); + background-color: var(--color-accent-light); } - /* Aktiv-Indikator: kleiner Akzentstreifen links */ + /* Akzentstreifen links */ .nav-sidebar .nav-item[aria-current="page"]::before { content: ''; position: absolute; left: 0; - top: 20%; - bottom: 20%; + top: 6px; + bottom: 6px; width: 3px; border-radius: 0 var(--radius-full) var(--radius-full) 0; - background: linear-gradient(180deg, var(--color-accent) 0%, #5856D6 100%); - box-shadow: 0 0 6px rgba(0, 122, 255, 0.45); + background: var(--color-accent); } - /* ── Divider vor Einstellungen ── */ + /* Active-Press auf Desktop */ + .nav-sidebar .nav-item:active { + transform: scale(0.97); + } + + /* ── Einstellungen ans Ende ── */ .nav-sidebar__items > a:last-child { margin-top: auto; } } -/* -------------------------------------------------------- +/* ================================================================ * Sidebar Expanded (≥ 1280px) — Labels sichtbar - * -------------------------------------------------------- */ + * ================================================================ */ @media (min-width: 1280px) { :root { --sidebar-width: var(--sidebar-width-expanded); @@ -267,55 +302,45 @@ /* Logo: Text-Variante */ .nav-sidebar__logo { justify-content: flex-start; - padding: var(--space-3) var(--space-4) var(--space-5); + padding: var(--space-2) var(--space-5) var(--space-4); gap: var(--space-3); } .nav-sidebar__logo::before { - width: 30px; - height: 30px; + width: 28px; + height: 28px; font-size: var(--text-xs); - flex-shrink: 0; } .nav-sidebar__logo > span { display: inline-block; font-size: var(--text-lg); font-weight: var(--font-weight-bold); - letter-spacing: -0.4px; - background: linear-gradient(135deg, var(--color-accent) 0%, #5856D6 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; + letter-spacing: -0.3px; + color: var(--color-text-primary); } .nav-sidebar__items { - padding: 0 var(--space-3); - gap: 2px; + padding: 0 var(--space-2); + gap: var(--space-0h); } /* Nav-Items: horizontal mit Label */ .nav-sidebar .nav-item { justify-content: flex-start; - padding: 6px var(--space-3); + padding: 8px var(--space-3); gap: var(--space-3); - min-height: unset; } .nav-sidebar .nav-item__icon { - width: 17px; - height: 17px; + width: 18px; + height: 18px; } .nav-sidebar .nav-item__label { display: block; font-weight: inherit; } - - /* Aktiv-Indikator bleibt links */ - .nav-sidebar .nav-item[aria-current="page"]::before { - left: 0; - } } /* -------------------------------------------------------- @@ -331,28 +356,49 @@ display: flex; align-items: center; justify-content: space-between; - margin-bottom: var(--space-6); + margin-bottom: var(--space-5); gap: var(--space-4); } .page__title { - font-size: var(--text-2xl); + font-size: var(--text-xl); font-weight: var(--font-weight-bold); + letter-spacing: -0.3px; } @media (min-width: 1024px) { .page { - padding: var(--space-10) var(--space-10); + padding: var(--space-8) var(--space-8); } .page__title { - font-size: var(--text-3xl); + font-size: var(--text-2xl); letter-spacing: -0.5px; } } +@media (min-width: 1440px) { + .page { + padding: var(--space-8) var(--space-10); + } +} + /* -------------------------------------------------------- * Cards + * + * Einheitliches Card-Pattern für alle Module: + * - Padding: 16px (mobile), 20px (desktop) + * - Radius: 12px + * - Shadow: subtle (sm) + * - Internes Spacing: 8–12px + * - Hover auf Desktop: leichter Lift (1px) + * + * Varianten: + * .card — Basis (kein Padding) + * .card--padded — Mit Padding + * .card--compact — Enges Padding (12px) + * .card--flat — Kein Shadow, nur Border + * .card--interactive — Hover-Lift + Cursor * -------------------------------------------------------- */ .card { background-color: var(--color-surface); @@ -365,6 +411,168 @@ padding: var(--space-4); } +.card--compact { + padding: var(--space-3); +} + +.card--flat { + box-shadow: none; + border: 1px solid var(--color-border-subtle); +} + +.card--interactive { + cursor: pointer; + transition: + transform var(--transition-fast), + box-shadow var(--transition-fast); +} + +.card--interactive:hover { + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.card--interactive:active { + transform: scale(0.99); +} + +@media (min-width: 1024px) { + .card--padded { + padding: var(--space-5); + } +} + +/* ── Card-Inhalts-Utilities ── */ +.card__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-3); + padding-bottom: var(--space-3); + margin-bottom: var(--space-3); + border-bottom: 1px solid var(--color-border-subtle); +} + +.card__title { + font-size: var(--text-sm); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); +} + +.card__body { + display: flex; + flex-direction: column; + gap: var(--space-2); +} + +/* -------------------------------------------------------- + * Modal-Basis + * + * Einheitliches Modal-Pattern für alle Module: + * - Mobile: Bottom-Sheet (gerundet oben) + * - Desktop: Zentriert (max-width variabel) + * - Overlay: var(--color-overlay) + * - Padding: 16px + * - Radius: 16px + * -------------------------------------------------------- */ +.modal-overlay { + position: fixed; + inset: 0; + background-color: var(--color-overlay); + z-index: var(--z-modal); + display: flex; + align-items: flex-end; + justify-content: center; + animation: modal-overlay-in 0.2s ease forwards; +} + +@media (min-width: 768px) { + .modal-overlay { + align-items: center; + padding: var(--space-6); + } +} + +.modal-panel { + background-color: var(--color-surface); + width: 100%; + max-height: 90dvh; + overflow-y: auto; + border-radius: var(--radius-lg) var(--radius-lg) 0 0; + animation: modal-slide-up 0.25s var(--ease-out) forwards; +} + +@media (min-width: 768px) { + .modal-panel { + max-width: 520px; + border-radius: var(--radius-lg); + animation: modal-scale-in 0.2s var(--ease-out) forwards; + } +} + +.modal-panel__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-4); + border-bottom: 1px solid var(--color-border-subtle); + position: sticky; + top: 0; + background-color: var(--color-surface); + z-index: 1; +} + +.modal-panel__title { + font-size: var(--text-md); + font-weight: var(--font-weight-semibold); +} + +.modal-panel__close { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-full); + color: var(--color-text-secondary); + transition: background-color var(--transition-fast); +} + +.modal-panel__close:hover { + background-color: var(--color-surface-3); +} + +.modal-panel__body { + padding: var(--space-4); + display: flex; + flex-direction: column; + gap: var(--space-4); +} + +.modal-panel__footer { + display: flex; + align-items: center; + justify-content: flex-end; + gap: var(--space-3); + padding: var(--space-3) var(--space-4); + border-top: 1px solid var(--color-border-subtle); +} + +@keyframes modal-overlay-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes modal-slide-up { + from { transform: translateY(100%); } + to { transform: translateY(0); } +} + +@keyframes modal-scale-in { + from { opacity: 0; transform: scale(0.95); } + to { opacity: 1; transform: scale(1); } +} + /* -------------------------------------------------------- * Buttons * -------------------------------------------------------- */ @@ -373,31 +581,50 @@ align-items: center; justify-content: center; gap: var(--space-2); - padding: var(--space-3) var(--space-4); + padding: var(--space-2) var(--space-4); border-radius: var(--radius-sm); - font-size: var(--text-base); + font-size: var(--text-sm); font-weight: var(--font-weight-medium); - min-height: 44px; - transition: opacity var(--transition-fast), background-color var(--transition-fast); + min-height: 36px; + transition: + background-color var(--transition-fast), + box-shadow var(--transition-fast), + transform var(--transition-fast); cursor: pointer; white-space: nowrap; } +/* Touch-Targets: 44px auf Mobile */ +@media (max-width: 1023px) { + .btn { + min-height: 44px; + } +} + +.btn:active { + transform: scale(0.98); +} + .btn--primary { background-color: var(--color-btn-primary); color: #ffffff; - box-shadow: 0 1px 2px rgba(0,0,0,0.12); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } .btn--primary:hover { background-color: var(--color-btn-primary-hover); - box-shadow: 0 2px 6px rgba(0,102,219,0.28); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); } .btn--secondary { background-color: transparent; color: var(--color-accent); - border: 1.5px solid var(--color-accent); + border: 1.5px solid var(--color-border); +} + +.btn--secondary:hover { + background-color: var(--color-accent-light); + border-color: var(--color-accent); } .btn--danger { @@ -405,6 +632,10 @@ color: #ffffff; } +.btn--danger:hover { + background-color: var(--color-danger-hover); +} + .btn--ghost { background-color: transparent; color: var(--color-text-secondary); @@ -412,27 +643,36 @@ .btn--ghost:hover { background-color: var(--color-surface-2); + color: var(--color-text-primary); } .btn:disabled { opacity: 0.4; cursor: not-allowed; + pointer-events: none; } .btn--icon { padding: var(--space-2); - min-height: 44px; - min-width: 44px; + min-height: 36px; + min-width: 36px; border-radius: var(--radius-sm); } +@media (max-width: 1023px) { + .btn--icon { + min-height: 44px; + min-width: 44px; + } +} + /* FAB (Floating Action Button) */ .fab { position: fixed; bottom: calc(var(--nav-height-mobile) + var(--safe-area-inset-bottom) + var(--space-4)); right: var(--space-4); - width: 56px; - height: 56px; + width: 52px; + height: 52px; border-radius: var(--radius-full); background-color: var(--color-accent); color: #ffffff; @@ -441,17 +681,24 @@ align-items: center; justify-content: center; z-index: calc(var(--z-nav) - 1); - transition: transform var(--transition-fast), box-shadow var(--transition-fast); + transition: + transform var(--transition-fast), + box-shadow var(--transition-fast); } .fab:hover { transform: scale(1.05); - box-shadow: var(--shadow-lg); +} + +.fab:active { + transform: scale(0.95); } @media (min-width: 1024px) { .fab { bottom: var(--space-8); + width: 48px; + height: 48px; } } @@ -460,19 +707,27 @@ * -------------------------------------------------------- */ .input { width: 100%; - padding: var(--space-3) var(--space-4); + padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); border: 1.5px solid var(--color-border); background-color: var(--color-surface); color: var(--color-text-primary); - font-size: var(--text-base); - transition: border-color var(--transition-fast); + font-size: var(--text-md); + transition: border-color var(--transition-fast), box-shadow var(--transition-fast); min-height: 44px; } +@media (min-width: 1024px) { + .input { + min-height: 36px; + font-size: var(--text-base); + } +} + .input:focus { outline: none; border-color: var(--color-accent); + box-shadow: 0 0 0 3px var(--color-accent-light); } .input::placeholder { @@ -500,9 +755,9 @@ .skeleton { background: linear-gradient( 90deg, - var(--color-border) 25%, + var(--color-border-subtle) 25%, var(--color-surface-2) 50%, - var(--color-border) 75% + var(--color-border-subtle) 75% ); background-size: 200% 100%; animation: skeleton-shimmer 1.5s infinite; @@ -522,31 +777,32 @@ flex-direction: column; align-items: center; justify-content: center; - gap: var(--space-4); + gap: var(--space-3); padding: var(--space-12) var(--space-6); text-align: center; } .empty-state__icon { - width: 64px; - height: 64px; + width: 56px; + height: 56px; color: var(--color-text-disabled); } .empty-state__title { - font-size: var(--text-lg); + font-size: var(--text-md); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); } .empty-state__description { - font-size: var(--text-base); + font-size: var(--text-sm); color: var(--color-text-secondary); max-width: 280px; + line-height: var(--line-height-base); } /* -------------------------------------------------------- - * Responsive Grid + * Responsive Grid (Utility) * -------------------------------------------------------- */ .grid { display: grid; @@ -562,6 +818,156 @@ .grid--3 { grid-template-columns: repeat(3, 1fr); } } +@media (min-width: 1440px) { + .grid--4 { grid-template-columns: repeat(4, 1fr); } +} + +/* -------------------------------------------------------- + * Layout-Primitives + * + * Wiederverwendbare Content-Area-Patterns für Desktop. + * Mobile: immer single-column (Stacking). + * + * .layout-master-detail — Liste links, Detail rechts (Aufgaben, Einkauf) + * .layout-content-aside — Hauptinhalt + schmale Seitenleiste (Kalender) + * .layout-center — Zentrierter schmaler Content (Settings, Login) + * .layout-wide — Volle Breite mit max-width (Dashboard) + * -------------------------------------------------------- */ + +/* ── Master-Detail ── + * Mobile: gestapelt (Detail wird programmatisch ein-/ausgeblendet). + * Desktop: 2 Spalten — Liste ~40%, Detail ~60%. + */ +.layout-master-detail { + display: flex; + flex-direction: column; + gap: var(--space-4); + max-width: var(--content-max-width); + margin: 0 auto; +} + +.layout-master-detail__master { + flex: 1; + min-width: 0; +} + +.layout-master-detail__detail { + flex: 1; + min-width: 0; +} + +@media (min-width: 1024px) { + .layout-master-detail { + flex-direction: row; + gap: var(--space-6); + } + + .layout-master-detail__master { + flex: 0 0 380px; + max-width: 420px; + overflow-y: auto; + max-height: calc(100dvh - var(--space-16)); + position: sticky; + top: var(--space-8); + } + + .layout-master-detail__detail { + flex: 1; + } +} + +@media (min-width: 1440px) { + .layout-master-detail__master { + flex: 0 0 420px; + max-width: 460px; + } +} + +/* ── Content + Aside ── + * Mobile: gestapelt. + * Desktop: Hauptinhalt nimmt Platz ein, Aside ist 280–320px. + */ +.layout-content-aside { + display: flex; + flex-direction: column; + gap: var(--space-4); + max-width: var(--content-max-width); + margin: 0 auto; +} + +.layout-content-aside__main { + flex: 1; + min-width: 0; +} + +.layout-content-aside__aside { + min-width: 0; +} + +@media (min-width: 1024px) { + .layout-content-aside { + flex-direction: row; + gap: var(--space-6); + } + + .layout-content-aside__main { + flex: 1; + } + + .layout-content-aside__aside { + flex: 0 0 280px; + overflow-y: auto; + max-height: calc(100dvh - var(--space-16)); + position: sticky; + top: var(--space-8); + } +} + +@media (min-width: 1440px) { + .layout-content-aside__aside { + flex: 0 0 320px; + } +} + +/* ── Center ── + * Schmaler, zentrierter Content-Bereich (max. 720px). + * Ideal für Settings, Formulare, Einzelansichten. + */ +.layout-center { + max-width: 720px; + margin: 0 auto; +} + +/* ── Wide ── + * Volle verfügbare Breite mit max-width. + * Für Dashboard und andere Multi-Column-Ansichten. + */ +.layout-wide { + max-width: var(--content-max-width); + margin: 0 auto; +} + +/* ── Prose ── + * Text-Content mit optimaler Lesebreite. + */ +.prose { + max-width: 720px; +} + +/* ── Sticky-Header (Modul-Toolbars) ── + * Klebt am oberen Rand beim Scrollen. + */ +.sticky-header { + position: sticky; + top: 0; + z-index: var(--z-sticky); + background-color: color-mix(in srgb, var(--color-bg) 90%, transparent); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + padding-bottom: var(--space-3); + margin-bottom: var(--space-4); +} + /* -------------------------------------------------------- * Toast-Benachrichtigungen * -------------------------------------------------------- */ @@ -575,25 +981,25 @@ gap: var(--space-2); z-index: var(--z-toast); pointer-events: none; - width: min(calc(100% - var(--space-8)), 400px); + width: min(calc(100% - var(--space-8)), 380px); } @media (min-width: 1024px) { .toast-container { bottom: var(--space-6); - left: calc(var(--sidebar-width) + 50%); + left: calc(var(--sidebar-width) + (100% - var(--sidebar-width)) / 2); } } .toast { - background-color: var(--color-text-primary); - color: var(--color-bg); + background-color: var(--neutral-800); + color: var(--neutral-50); padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); font-size: var(--text-sm); box-shadow: var(--shadow-lg); pointer-events: auto; - animation: toast-in 0.2s ease forwards; + animation: toast-in 0.25s var(--ease-out) forwards; } .toast--success { background-color: var(--color-success); color: #fff; } @@ -601,6 +1007,60 @@ .toast--warning { background-color: var(--color-warning); color: #fff; } @keyframes toast-in { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } + from { opacity: 0; transform: translateY(6px) scale(0.98); } + to { opacity: 1; transform: translateY(0) scale(1); } +} + +.toast--out { + animation: toast-out 0.2s ease forwards; +} + +@keyframes toast-out { + from { opacity: 1; transform: scale(1); } + to { opacity: 0; transform: scale(0.95) translateY(4px); } +} + +/* -------------------------------------------------------- + * Print-Styles + * -------------------------------------------------------- */ +@media print { + .nav-sidebar, + .nav-bottom, + .fab, + .toast-container, + .modal-overlay, + .modal-backdrop { + display: none !important; + } + + .app-content { + margin-left: 0 !important; + padding-bottom: 0 !important; + } + + .app-shell { + display: block; + } + + body { + background: #fff; + color: #000; + } + + .card { + box-shadow: none; + border: 1px solid #ddd; + break-inside: avoid; + } + + a[href]::after { + content: " (" attr(href) ")"; + font-size: 0.85em; + color: #666; + } + + .nav-item, + a[data-route]::after { + content: none; + } } diff --git a/public/styles/meals.css b/public/styles/meals.css index 618c072..d81f27c 100644 --- a/public/styles/meals.css +++ b/public/styles/meals.css @@ -65,7 +65,7 @@ } .day-column { - border-bottom: 1px solid var(--color-border); + border-bottom: 1px solid var(--color-border-subtle); } .day-column:last-child { @@ -103,14 +103,14 @@ .day-slots { display: grid; - grid-template-columns: repeat(4, 1fr); + grid-template-columns: repeat(2, 1fr); gap: var(--space-2); - padding: 0 var(--space-4) var(--space-4); + padding: 0 var(--space-4) var(--space-3); } -@media (max-width: 600px) { +@media (min-width: 600px) { .day-slots { - grid-template-columns: repeat(2, 1fr); + grid-template-columns: repeat(4, 1fr); } } @@ -118,7 +118,7 @@ * Mahlzeit-Slot * -------------------------------------------------------- */ .meal-slot { - min-height: 80px; + min-height: 72px; border-radius: var(--radius-sm); border: 1.5px dashed var(--color-border); background-color: var(--color-surface); @@ -142,11 +142,11 @@ padding: var(--space-1) var(--space-2) 0; } -/* Slot-Typ-Farben */ -.meal-slot[data-type="breakfast"] .meal-slot__type-label { color: #FF9500; } -.meal-slot[data-type="lunch"] .meal-slot__type-label { color: #34C759; } -.meal-slot[data-type="dinner"] .meal-slot__type-label { color: #007AFF; } -.meal-slot[data-type="snack"] .meal-slot__type-label { color: #FF6B35; } +/* Slot-Typ-Farben — zentrale Tokens aus tokens.css */ +.meal-slot[data-type="breakfast"] .meal-slot__type-label { color: var(--meal-breakfast); } +.meal-slot[data-type="lunch"] .meal-slot__type-label { color: var(--meal-lunch); } +.meal-slot[data-type="dinner"] .meal-slot__type-label { color: var(--meal-dinner); } +.meal-slot[data-type="snack"] .meal-slot__type-label { color: var(--meal-snack); } .meal-slot__add-btn { flex: 1; @@ -236,7 +236,7 @@ .meal-card__action-btn:hover { color: var(--color-danger); - background-color: var(--color-surface-2, rgba(0,0,0,0.04)); + background-color: var(--color-surface-2); } .meal-card__action-btn--shopping:hover { @@ -249,7 +249,7 @@ .meal-modal-overlay { position: fixed; inset: 0; - background-color: rgba(0, 0, 0, 0.5); + background-color: var(--color-overlay); z-index: var(--z-modal); display: flex; align-items: flex-end; @@ -483,10 +483,10 @@ border-radius: var(--radius-full); } -.meal-type-badge--breakfast { background: #FFF3E0; color: #FF9500; } -.meal-type-badge--lunch { background: #E8F5E9; color: #2E7D32; } -.meal-type-badge--dinner { background: #E3F2FF; color: #007AFF; } -.meal-type-badge--snack { background: #FBE9E7; color: #FF6B35; } +.meal-type-badge--breakfast { background: var(--meal-breakfast-light); color: var(--meal-breakfast); } +.meal-type-badge--lunch { background: var(--meal-lunch-light); color: var(--meal-lunch); } +.meal-type-badge--dinner { background: var(--meal-dinner-light); color: var(--meal-dinner); } +.meal-type-badge--snack { background: var(--meal-snack-light); color: var(--meal-snack); } /* -------------------------------------------------------- * Leer-Zustand diff --git a/public/styles/notes.css b/public/styles/notes.css index ec9697d..7ba3a4c 100644 --- a/public/styles/notes.css +++ b/public/styles/notes.css @@ -77,12 +77,12 @@ } .note-card:hover { - transform: translateY(-2px); + transform: translateY(-1px); box-shadow: var(--shadow-md); } .note-card--pinned { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + box-shadow: var(--shadow-md); } .note-card__pin { @@ -92,7 +92,7 @@ width: 24px; height: 24px; border-radius: var(--radius-full); - background: rgba(0,0,0,0.1); + background: var(--color-overlay-light); border: none; cursor: pointer; display: flex; @@ -101,12 +101,12 @@ min-height: unset; opacity: 0; transition: opacity var(--transition-fast), background-color var(--transition-fast); - color: rgba(0,0,0,0.6); + color: var(--color-text-primary); } .note-card--pinned .note-card__pin { opacity: 1; - background: rgba(0,0,0,0.15); + background: var(--color-overlay-light); } .note-card:hover .note-card__pin { @@ -114,21 +114,21 @@ } .note-card__pin:hover { - background: rgba(0,0,0,0.2); + background: var(--color-overlay); } .note-card__title { font-size: var(--text-sm); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-1); - color: rgba(0,0,0,0.8); + color: var(--color-text-primary); padding-right: var(--space-6); } .note-card__content { font-size: var(--text-sm); line-height: var(--line-height-relaxed); - color: rgba(0,0,0,0.75); + color: var(--color-text-secondary); word-break: break-word; white-space: pre-wrap; } @@ -143,7 +143,7 @@ justify-content: space-between; margin-top: var(--space-2); padding-top: var(--space-2); - border-top: 1px solid rgba(0,0,0,0.08); + border-top: 1px solid var(--color-border-subtle); } .note-card__creator { @@ -151,7 +151,7 @@ align-items: center; gap: var(--space-1); font-size: var(--text-xs); - color: rgba(0,0,0,0.5); + color: var(--color-text-tertiary); } .note-card__avatar { @@ -171,14 +171,14 @@ width: 24px; height: 24px; border-radius: var(--radius-full); - background: rgba(0,0,0,0.08); + background: var(--color-overlay-light); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; min-height: unset; - color: rgba(0,0,0,0.5); + color: var(--color-text-tertiary); opacity: 0; transition: opacity var(--transition-fast), background-color var(--transition-fast); } @@ -188,7 +188,7 @@ } .note-card__delete:hover { - background: rgba(255,59,48,0.2); + background: var(--color-danger-light); color: var(--color-danger); } @@ -219,7 +219,7 @@ .note-modal-overlay { position: fixed; inset: 0; - background-color: rgba(0,0,0,0.5); + background-color: var(--color-overlay); z-index: var(--z-modal); display: flex; align-items: flex-end; diff --git a/public/styles/reset.css b/public/styles/reset.css index aa87e1b..7f79aa6 100644 --- a/public/styles/reset.css +++ b/public/styles/reset.css @@ -19,6 +19,8 @@ html { -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; scroll-behavior: smooth; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } body { @@ -69,6 +71,16 @@ button, [role="button"], input[type="checkbox"], input[type="radio"] { border-radius: var(--radius-xs); } +/* Reduzierte Bewegung (Barrierefreiheit) */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + /* Versteckte, aber zugängliche Elemente */ .sr-only { position: absolute; diff --git a/public/styles/settings.css b/public/styles/settings.css index 090087f..8a860c5 100644 --- a/public/styles/settings.css +++ b/public/styles/settings.css @@ -1,11 +1,11 @@ /** * Modul: Einstellungen (Settings) * Zweck: Styles für die Settings-Seite - * Abhängigkeiten: tokens.css + * Abhängigkeiten: tokens.css, layout.css */ /* -------------------------------------------------------- - Seiten-Layout + Seiten-Layout — nutzt layout-center (max 720px) -------------------------------------------------------- */ .settings-page { @@ -18,23 +18,23 @@ -------------------------------------------------------- */ .settings-banner { - padding: 12px 16px; + padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); - margin-bottom: 16px; - font-size: 14px; - font-weight: 500; + margin-bottom: var(--space-4); + font-size: var(--text-sm); + font-weight: var(--font-weight-medium); } .settings-banner--success { - background: rgba(52, 199, 89, 0.12); + background: var(--color-success-light); color: var(--color-success); - border: 1px solid rgba(52, 199, 89, 0.3); + border: 1px solid var(--color-success); } .settings-banner--error { - background: rgba(255, 59, 48, 0.1); + background: var(--color-danger-light); color: var(--color-danger); - border: 1px solid rgba(255, 59, 48, 0.25); + border: 1px solid var(--color-danger); } /* -------------------------------------------------------- @@ -42,16 +42,16 @@ -------------------------------------------------------- */ .settings-section { - margin-bottom: 32px; + margin-bottom: var(--space-8); } .settings-section__title { - font-size: 13px; - font-weight: 600; + font-size: var(--text-xs); + font-weight: var(--font-weight-semibold); text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--color-text-secondary); - margin: 0 0 10px 4px; + letter-spacing: 0.05em; + color: var(--color-text-tertiary); + margin: 0 0 var(--space-2) var(--space-1); } /* -------------------------------------------------------- @@ -62,8 +62,14 @@ background: var(--color-surface); border-radius: var(--radius-md); box-shadow: var(--shadow-sm); - padding: 20px; - margin-bottom: 12px; + padding: var(--space-4); + margin-bottom: var(--space-3); +} + +@media (min-width: 1024px) { + .settings-card { + padding: var(--space-5); + } } .settings-card--hidden { @@ -71,9 +77,9 @@ } .settings-card__title { - font-size: 15px; - font-weight: 600; - margin: 0 0 16px; + font-size: var(--text-base); + font-weight: var(--font-weight-semibold); + margin: 0 0 var(--space-3); color: var(--color-text-primary); } @@ -84,17 +90,17 @@ .settings-user-info { display: flex; align-items: center; - gap: 16px; + gap: var(--space-3); } .settings-user-info__name { - font-size: 17px; - font-weight: 600; + font-size: var(--text-md); + font-weight: var(--font-weight-semibold); color: var(--color-text-primary); } .settings-user-info__username { - font-size: 13px; + font-size: var(--text-sm); color: var(--color-text-secondary); margin-top: 2px; } @@ -104,23 +110,23 @@ -------------------------------------------------------- */ .settings-avatar { - width: 48px; - height: 48px; - border-radius: 50%; + width: 44px; + height: 44px; + border-radius: var(--radius-full); display: flex; align-items: center; justify-content: center; - font-size: 18px; - font-weight: 700; + font-size: var(--text-lg); + font-weight: var(--font-weight-bold); color: #fff; flex-shrink: 0; user-select: none; } .settings-avatar--sm { - width: 36px; - height: 36px; - font-size: 13px; + width: 32px; + height: 32px; + font-size: var(--text-sm); } /* -------------------------------------------------------- @@ -130,30 +136,30 @@ .settings-form { display: flex; flex-direction: column; - gap: 14px; + gap: var(--space-3); } .settings-form-actions { display: flex; - gap: 10px; + gap: var(--space-2); flex-wrap: wrap; } .form-error { - font-size: 13px; + font-size: var(--text-sm); color: var(--color-danger); - padding: 8px 12px; - background: rgba(255, 59, 48, 0.08); + padding: var(--space-2) var(--space-3); + background: var(--color-danger-light); border-radius: var(--radius-sm); } .form-hint { - font-size: 13px; + font-size: var(--text-sm); color: var(--color-text-secondary); } .form-input--color { - padding: 4px 8px; + padding: var(--space-1) var(--space-2); height: 44px; cursor: pointer; } @@ -165,13 +171,13 @@ .settings-sync-header { display: flex; align-items: center; - gap: 14px; - margin-bottom: 14px; + gap: var(--space-3); + margin-bottom: var(--space-3); } .settings-sync-logo { - width: 40px; - height: 40px; + width: 36px; + height: 36px; border-radius: var(--radius-sm); display: flex; align-items: center; @@ -180,23 +186,23 @@ } .settings-sync-logo--google { - background: #f8f9fa; + background: var(--color-surface-2); border: 1px solid var(--color-border); } .settings-sync-logo--apple { - background: #1c1c1e; - color: #fff; + background: var(--neutral-900); + color: var(--neutral-50); } .settings-sync-info__name { - font-size: 15px; - font-weight: 600; + font-size: var(--text-base); + font-weight: var(--font-weight-semibold); color: var(--color-text-primary); } .settings-sync-info__status { - font-size: 13px; + font-size: var(--text-sm); color: var(--color-text-secondary); margin-top: 2px; } @@ -207,7 +213,7 @@ .settings-sync-actions { display: flex; - gap: 10px; + gap: var(--space-2); flex-wrap: wrap; } @@ -217,17 +223,17 @@ .settings-members { list-style: none; - margin: 0 0 16px; + margin: 0 0 var(--space-4); padding: 0; display: flex; flex-direction: column; - gap: 10px; + gap: var(--space-3); } .settings-member { display: flex; align-items: center; - gap: 12px; + gap: var(--space-3); } .settings-member__info { @@ -237,8 +243,8 @@ .settings-member__name { display: block; - font-size: 15px; - font-weight: 500; + font-size: var(--text-base); + font-weight: var(--font-weight-medium); color: var(--color-text-primary); white-space: nowrap; overflow: hidden; @@ -247,7 +253,7 @@ .settings-member__meta { display: block; - font-size: 12px; + font-size: var(--text-xs); color: var(--color-text-secondary); margin-top: 1px; } @@ -263,14 +269,3 @@ .settings-logout-btn { width: 100%; } - -/* -------------------------------------------------------- - Dark Mode - -------------------------------------------------------- */ - -@media (prefers-color-scheme: dark) { - .settings-sync-logo--google { - background: #2c2c2e; - border-color: var(--color-border); - } -} diff --git a/public/styles/shopping.css b/public/styles/shopping.css index 6d62bb4..b577ff7 100644 --- a/public/styles/shopping.css +++ b/public/styles/shopping.css @@ -55,6 +55,11 @@ min-height: 36px; } +.list-tab:hover:not(.list-tab--active) { + border-color: var(--color-text-secondary); + color: var(--color-text-primary); +} + .list-tab--active { background-color: var(--color-accent); border-color: var(--color-accent); @@ -149,7 +154,7 @@ .quick-add__input { width: 100%; padding: var(--space-3) var(--space-4); - padding-right: 80px; + padding-right: var(--space-20, 80px); border-radius: var(--radius-sm); border: 1.5px solid var(--color-border); background-color: var(--color-surface); @@ -187,7 +192,9 @@ } .quick-add__cat { - width: 130px; + width: auto; + min-width: 100px; + max-width: 160px; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); border: 1.5px solid var(--color-border); @@ -274,7 +281,7 @@ text-transform: uppercase; letter-spacing: 0.06em; padding: var(--space-2) 0 var(--space-1); - border-bottom: 1px solid var(--color-border); + border-bottom: 1px solid var(--color-border-subtle); margin-bottom: var(--space-1); display: flex; align-items: center; @@ -293,10 +300,10 @@ display: flex; align-items: center; gap: var(--space-3); - padding: var(--space-3) var(--space-1); - border-bottom: 1px solid var(--color-border); + padding: var(--space-2) var(--space-1); + border-bottom: 1px solid var(--color-border-subtle); transition: opacity var(--transition-fast); - min-height: 48px; + min-height: 44px; } .shopping-item:last-child { border-bottom: none; } @@ -324,6 +331,13 @@ .item-check--checked { background-color: var(--color-success); border-color: var(--color-success); + animation: check-pop 0.2s var(--ease-out); +} + +@keyframes check-pop { + 0% { transform: scale(1); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } } .item-check__icon { diff --git a/public/styles/tasks.css b/public/styles/tasks.css index 171105b..68a7f25 100644 --- a/public/styles/tasks.css +++ b/public/styles/tasks.css @@ -65,6 +65,10 @@ cursor: pointer; } +.group-toggle__btn:hover:not(.group-toggle__btn--active) { + color: var(--color-text-primary); +} + .group-toggle__btn--active { background-color: var(--color-surface); color: var(--color-text-primary); @@ -103,6 +107,11 @@ min-height: 34px; } +.filter-chip:hover:not(.filter-chip--active) { + border-color: var(--color-text-secondary); + color: var(--color-text-primary); +} + .filter-chip--active { background-color: var(--color-accent-light); border-color: var(--color-accent); @@ -118,7 +127,7 @@ border-radius: var(--radius-full); background-color: var(--color-accent); color: #fff; - font-size: 10px; + font-size: var(--text-2xs); line-height: 1; } @@ -232,13 +241,14 @@ .task-card--done { opacity: 0.6; + transition: opacity var(--transition-base); } .task-card__main { display: flex; align-items: flex-start; gap: var(--space-3); - padding: var(--space-4); + padding: var(--space-3); } /* Status-Checkbox */ @@ -261,6 +271,13 @@ .task-status-btn--done { border-color: var(--color-success); background-color: var(--color-success); + animation: check-pop 0.2s var(--ease-out); +} + +@keyframes check-pop { + 0% { transform: scale(1); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } } .task-status-btn--in_progress { @@ -312,10 +329,10 @@ border-radius: var(--radius-xs); } -.priority-badge--low { color: var(--color-priority-low); background-color: #8E8E9322; } -.priority-badge--medium { color: var(--color-priority-medium); background-color: #FF950022; } -.priority-badge--high { color: var(--color-priority-high); background-color: #FF6B3522; } -.priority-badge--urgent { color: var(--color-priority-urgent); background-color: #FF3B3022; } +.priority-badge--low { color: var(--color-priority-low); background-color: var(--color-priority-low-bg); } +.priority-badge--medium { color: var(--color-priority-medium); background-color: var(--color-priority-medium-bg); } +.priority-badge--high { color: var(--color-priority-high); background-color: var(--color-priority-high-bg); } +.priority-badge--urgent { color: var(--color-priority-urgent); background-color: var(--color-priority-urgent-bg); } .priority-dot { width: 6px; @@ -349,7 +366,7 @@ display: flex; align-items: center; justify-content: center; - font-size: 11px; + font-size: var(--text-2xs); font-weight: var(--font-weight-bold); color: #fff; flex-shrink: 0; @@ -357,7 +374,7 @@ /* Subtask-Fortschrittsbalken */ .subtask-progress { - padding: 0 var(--space-4) var(--space-3); + padding: 0 var(--space-3) var(--space-2); display: flex; align-items: center; gap: var(--space-2); @@ -387,8 +404,8 @@ /* Subtask-Liste (eingeklappt/ausgeklappt) */ .subtask-list { - border-top: 1px solid var(--color-border); - padding: var(--space-2) var(--space-4); + border-top: 1px solid var(--color-border-subtle); + padding: var(--space-2) var(--space-3); display: none; } @@ -399,7 +416,7 @@ align-items: center; gap: var(--space-2); padding: var(--space-2) 0; - border-bottom: 1px solid var(--color-border); + border-bottom: 1px solid var(--color-border-subtle); } .subtask-item:last-child { border-bottom: none; } @@ -461,7 +478,7 @@ .modal-backdrop { position: fixed; inset: 0; - background-color: rgba(0, 0, 0, 0.4); + background-color: var(--color-overlay); z-index: var(--z-modal); display: flex; align-items: flex-end; @@ -551,7 +568,7 @@ border-radius: var(--radius-full); background-color: var(--color-danger); color: #fff; - font-size: 10px; + font-size: var(--text-2xs); font-weight: var(--font-weight-bold); margin-left: auto; } @@ -561,15 +578,15 @@ * -------------------------------------------------------- */ .kanban-board { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: 1fr; gap: var(--space-3); align-items: start; min-height: 60vh; } -@media (max-width: 640px) { +@media (min-width: 640px) { .kanban-board { - grid-template-columns: 1fr; + grid-template-columns: repeat(3, 1fr); } } diff --git a/public/styles/tokens.css b/public/styles/tokens.css index 36a0e58..0b787be 100644 --- a/public/styles/tokens.css +++ b/public/styles/tokens.css @@ -1,59 +1,147 @@ /** * Modul: Design Tokens - * Zweck: CSS Custom Properties für das gesamte Design-System + * Zweck: CSS Custom Properties fuer das gesamte Design-System * Abhängigkeiten: keine + * + * Aufbau: + * 1. Neutral-Farbskala (50–950) + * 2. Akzentfarben + Semantische Farben + * 3. Modul-Akzentfarben + * 4. Mahlzeit-Typ-Farben + * 5. Prioritäten + * 6. Overlay / Backdrop + * 7. Schatten + * 8. Border-Radien + * 9. Typografie + * 10. Abstände (4px-Raster) + * 11. Layout + * 12. Sidebar + * 13. Übergänge + * 14. Z-Indizes + * 15. Dark Mode */ :root { /* -------------------------------------------------------- - * Farben — Neutrals + * 1. Farben — Neutral-Skala + * Leicht warmgetönt (kein reines Grau) für einladende Atmosphäre. + * Benannt als --neutral-{stufe} für direkte Nutzung, + * plus semantische Aliase (--color-bg, --color-surface etc.) + * die bestehender Code bereits referenziert. * -------------------------------------------------------- */ - --color-bg: #F5F5F7; - --color-surface: #FFFFFF; - --color-surface-2: #F0F0F5; - --color-border: #E5E5EA; - --color-text-primary: #1C1C1E; - --color-text-secondary: #6C6C70; /* WCAG AA: ~5.2:1 auf weißem Hintergrund */ - --color-text-disabled: #C7C7CC; + --neutral-50: #FAFAF8; + --neutral-100: #F5F4F1; + --neutral-150: #EFEEE9; + --neutral-200: #E8E7E2; + --neutral-250: #DDDCD7; + --neutral-300: #D1D0CB; + --neutral-400: #B5B4AF; + --neutral-500: #8E8D89; + --neutral-600: #6C6B67; + --neutral-700: #4A4A46; + --neutral-800: #2E2E2B; + --neutral-900: #1C1C1A; + --neutral-950: #121211; + + /* Semantische Aliase (abwärtskompatibel) */ + --color-bg: var(--neutral-100); + --color-surface: #FFFFFF; + --color-surface-2: var(--neutral-50); + --color-surface-3: var(--neutral-150); + --color-border: var(--neutral-200); + --color-border-subtle: var(--neutral-150); + --color-text-primary: var(--neutral-900); + --color-text-secondary: var(--neutral-600); /* WCAG AA: ~5.0:1 auf weiß */ + --color-text-tertiary: var(--neutral-500); + --color-text-disabled: var(--neutral-300); /* -------------------------------------------------------- - * Farben — Akzent (konfigurierbar) + * 2. Farben — Akzent (konfigurierbar) + * Wärmerer Blauton statt reinem Corporate-Blau. * -------------------------------------------------------- */ - --color-accent: #007AFF; - --color-accent-hover: #0056CC; - --color-accent-light: #E3F2FF; - --color-btn-primary: #0066DB; /* WCAG AA: ~5.0:1 auf weiß (Buttons mit weißem Text) */ - --color-btn-primary-hover: #0056CC; + --color-accent: #3478F6; + --color-accent-hover: #2860D0; + --color-accent-active: #1E4FA8; + --color-accent-light: #EBF2FF; + --color-accent-subtle: #D6E5FF; + --color-btn-primary: #2C6BE0; /* WCAG AA: ~4.8:1 auf weiß (weißer Text) */ + --color-btn-primary-hover: #2258BA; /* -------------------------------------------------------- - * Farben — Semantisch + * 3. Farben — Semantisch * -------------------------------------------------------- */ - --color-success: #34C759; - --color-success-light: #E3F9EB; - --color-warning: #FF9500; - --color-warning-light: #FFF3E0; - --color-danger: #FF3B30; - --color-danger-light: #FFE5E3; - --color-info: #5AC8FA; - --color-info-light: #E5F7FF; + --color-success: #2DA44E; + --color-success-hover: #258B3E; + --color-success-light: #DAFBE1; + --color-warning: #E6930A; + --color-warning-hover: #C97D08; + --color-warning-light: #FFF4D4; + --color-danger: #E5534B; + --color-danger-hover: #CC403A; + --color-danger-light: #FFE2E0; + --color-info: #54AEFF; + --color-info-hover: #3A94E5; + --color-info-light: #DDF4FF; /* -------------------------------------------------------- - * Farben — Prioritäten + * 4. Farben — Modul-Akzente + * Jedes Modul hat eine eigene dezente Akzentfarbe. + * Einsatz in Modul-Headern, Icons, aktiven States. * -------------------------------------------------------- */ - --color-priority-low: #8E8E93; - --color-priority-medium: #FF9500; - --color-priority-high: #FF6B35; - --color-priority-urgent: #FF3B30; + --module-dashboard: #3478F6; /* Blau — Übersicht, neutral */ + --module-tasks: #2DA44E; /* Grün — Erledigung, Fortschritt */ + --module-calendar: #8250DF; /* Violett — Termine, Zeit */ + --module-meals: #E6930A; /* Orange — Essen, Wärme */ + --module-shopping: #D4511E; /* Rot-Orange — Einkaufen, Aktion */ + --module-notes: #BF8700; /* Gold — Notizen, Pinnwand */ + --module-contacts: #0969DA; /* Kräftiges Blau — Kontakte */ + --module-budget: #1A7F5A; /* Teal — Finanzen, Stabilität */ + --module-settings: #6E7781; /* Grau — Konfiguration */ /* -------------------------------------------------------- - * Schatten + * 5. Farben — Mahlzeit-Typen + * Zentrale Tokens statt Hardcoding in meals.css * -------------------------------------------------------- */ - --shadow-sm: 0 1px 2px rgba(0,0,0,0.04), 0 1px 6px rgba(0,0,0,0.04); - --shadow-md: 0 4px 12px rgba(0,0,0,0.07), 0 1px 3px rgba(0,0,0,0.04); - --shadow-lg: 0 12px 32px rgba(0,0,0,0.09), 0 2px 8px rgba(0,0,0,0.05); + --meal-breakfast: #E6930A; + --meal-breakfast-light: #FFF4D4; + --meal-lunch: #2DA44E; + --meal-lunch-light: #DAFBE1; + --meal-dinner: #3478F6; + --meal-dinner-light: #EBF2FF; + --meal-snack: #D4511E; + --meal-snack-light: #FFECE3; /* -------------------------------------------------------- - * Border-Radien + * 6. Farben — Prioritäten + * -------------------------------------------------------- */ + --color-priority-low: var(--neutral-500); + --color-priority-medium: #E6930A; + --color-priority-high: #D4511E; + --color-priority-urgent: #E5534B; + + /* Hintergrundfarben für Priority-Badges */ + --color-priority-low-bg: rgba(142, 141, 137, 0.12); + --color-priority-medium-bg: rgba(230, 147, 10, 0.12); + --color-priority-high-bg: rgba(212, 81, 30, 0.12); + --color-priority-urgent-bg: rgba(229, 83, 75, 0.12); + + /* -------------------------------------------------------- + * 7. Overlay / Backdrop + * -------------------------------------------------------- */ + --color-overlay: rgba(0, 0, 0, 0.45); + --color-overlay-light: rgba(0, 0, 0, 0.2); + + /* -------------------------------------------------------- + * 8. Schatten + * 3 Stufen: subtle (Karten), medium (Dropdowns, Hover), + * elevated (Modals, FAB). + * -------------------------------------------------------- */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 4px rgba(0, 0, 0, 0.03); + --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.04); + + /* -------------------------------------------------------- + * 9. Border-Radien * -------------------------------------------------------- */ --radius-xs: 4px; --radius-sm: 8px; @@ -63,99 +151,153 @@ --radius-full: 9999px; /* -------------------------------------------------------- - * Typografie + * 10. Typografie + * System-Font-Stack laut Redesign-Spec. + * Skala von xs (12px) bis 4xl (36px). * -------------------------------------------------------- */ - --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI Variable', 'Segoe UI', system-ui, sans-serif; + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; --font-mono: 'SF Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', monospace; - --text-xs: 0.75rem; /* 12px */ - --text-sm: 0.8125rem; /* 13px */ - --text-base: 1rem; /* 16px */ - --text-md: 1.0625rem; /* 17px */ - --text-lg: 1.125rem; /* 18px */ - --text-xl: 1.25rem; /* 20px */ - --text-2xl: 1.5rem; /* 24px */ - --text-3xl: 1.875rem; /* 30px */ + /* Size-Skala */ + --text-2xs: 0.625rem; /* 10px — Micro-Labels (Nav, Avatare) */ + --text-xs: 0.75rem; /* 12px — Captions, Badges */ + --text-sm: 0.8125rem; /* 13px — Small/Secondary */ + --text-base: 0.875rem; /* 14px — Body (Desktop), kompakter */ + --text-md: 1rem; /* 16px — Body (Mobile), Inputs */ + --text-lg: 1.125rem; /* 18px — Section-Title */ + --text-xl: 1.25rem; /* 20px — Subtitle */ + --text-2xl: 1.5rem; /* 24px — Page-Title */ + --text-3xl: 1.875rem; /* 30px — Page-Title Desktop */ + --text-4xl: 2.25rem; /* 36px — Hero/Greeting */ - --line-height-tight: 1.2; - --line-height-base: 1.5; - --line-height-relaxed: 1.75; + /* Line-Heights */ + --line-height-tight: 1.25; + --line-height-snug: 1.375; + --line-height-base: 1.5; + --line-height-relaxed: 1.625; - --font-weight-regular: 400; - --font-weight-medium: 500; + /* Font-Weights */ + --font-weight-regular: 400; + --font-weight-medium: 500; --font-weight-semibold: 600; - --font-weight-bold: 700; + --font-weight-bold: 700; /* -------------------------------------------------------- - * Abstände + * 11. Abstände — 4px-Raster * -------------------------------------------------------- */ - --space-1: 4px; - --space-2: 8px; - --space-3: 12px; - --space-4: 16px; - --space-5: 20px; - --space-6: 24px; - --space-8: 32px; + --space-0: 0px; + --space-px: 1px; + --space-0h: 2px; + --space-1: 4px; + --space-2: 8px; + --space-3: 12px; + --space-4: 16px; + --space-5: 20px; + --space-6: 24px; + --space-8: 32px; --space-10: 40px; --space-12: 48px; --space-16: 64px; /* -------------------------------------------------------- - * Layout + * 12. Layout * -------------------------------------------------------- */ - --nav-height-mobile: 64px; - --sidebar-width: 64px; /* collapsed icon-only (1024–1279px) */ - --sidebar-width-expanded: 200px; /* full sidebar (1280px+) */ - --content-max-width: 1200px; + --nav-height-mobile: 56px; + --sidebar-width: 56px; /* collapsed icon-only (1024–1279px) */ + --sidebar-width-expanded: 220px; /* full sidebar (1280px+), max 240px laut Spec */ + --content-max-width: 1280px; - /* Sidebar Neumorphismus */ - --sidebar-bg: #E8E8EE; - --sidebar-shadow-light: rgba(255, 255, 255, 0.82); - --sidebar-shadow-dark: rgba(148, 148, 170, 0.28); + /* -------------------------------------------------------- + * 13. Sidebar + * Reduzierter Neumorphismus — subtilere Schatten. + * -------------------------------------------------------- */ + --sidebar-bg: var(--neutral-100); + --sidebar-shadow-light: rgba(255, 255, 255, 0.6); + --sidebar-shadow-dark: rgba(0, 0, 0, 0.08); --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px); /* -------------------------------------------------------- - * Übergänge + * 14. Übergänge + * fast=150ms, base=250ms, slow=400ms (laut Redesign-Spec) * -------------------------------------------------------- */ - --transition-fast: 0.1s ease; - --transition-base: 0.2s ease; - --transition-slow: 0.3s ease; + --transition-fast: 0.15s ease; + --transition-base: 0.25s ease; + --transition-slow: 0.4s ease; + + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); /* -------------------------------------------------------- - * Z-Indizes + * 15. Z-Indizes * -------------------------------------------------------- */ - --z-base: 0; - --z-card: 1; - --z-nav: 100; - --z-modal: 200; - --z-toast: 300; + --z-base: 0; + --z-card: 1; + --z-sticky: 10; + --z-dropdown: 50; + --z-nav: 100; + --z-modal: 200; + --z-toast: 300; } -/* -------------------------------------------------------- +/* ================================================================ * Dark Mode - * -------------------------------------------------------- */ + * ================================================================ */ @media (prefers-color-scheme: dark) { :root { - --color-bg: #1C1C1E; - --color-surface: #2C2C2E; - --color-surface-2: #3A3A3C; - --color-border: #3A3A3C; - --color-text-primary: #F5F5F7; - --color-text-secondary: #AEAEB2; /* WCAG AA: ~4.6:1 auf #2C2C2E */ - --color-text-disabled: #48484A; + /* Neutral-Skala invertiert (warm-dunkel) */ + --neutral-50: #1A1A18; + --neutral-100: #222220; + --neutral-150: #2A2A28; + --neutral-200: #333331; + --neutral-250: #3D3D3A; + --neutral-300: #48484A; + --neutral-400: #636360; + --neutral-500: #8E8D89; + --neutral-600: #AEADB0; /* WCAG AA: ~4.8:1 auf #2A2A28 */ + --neutral-700: #C8C7C3; + --neutral-800: #E2E1DC; + --neutral-900: #F5F4F1; + --neutral-950: #FAFAF8; - --sidebar-bg: #19191D; - --sidebar-shadow-light: rgba(255, 255, 255, 0.05); - --sidebar-shadow-dark: rgba(0, 0, 0, 0.55); + /* Semantische Aliase folgen automatisch via var(--neutral-*) */ + --color-surface: #2A2A28; + --color-surface-2: #1A1A18; + --color-surface-3: #333331; - --color-accent-light: #1A3A5C; - --color-success-light: #1A3A26; - --color-warning-light: #3A2800; - --color-danger-light: #3A1A1A; - --color-info-light: #1A3A4A; + /* Sidebar */ + --sidebar-bg: #1A1A18; + --sidebar-shadow-light: rgba(255, 255, 255, 0.04); + --sidebar-shadow-dark: rgba(0, 0, 0, 0.4); - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); - --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5); + /* Akzent-Light-Varianten (dunkler Hintergrund) */ + --color-accent-light: #1A2D4D; + --color-accent-subtle: #162442; + --color-success-light: #1A3325; + --color-warning-light: #332400; + --color-danger-light: #3D1C1A; + --color-info-light: #1A2D40; + + /* Modul-Akzente bleiben im Dark Mode gleich (genug Kontrast) */ + + /* Mahlzeit-Typ Light-Varianten */ + --meal-breakfast-light: #332400; + --meal-lunch-light: #1A3325; + --meal-dinner-light: #1A2D4D; + --meal-snack-light: #3D2010; + + /* Priority-Badge Hintergründe */ + --color-priority-low-bg: rgba(142, 141, 137, 0.18); + --color-priority-medium-bg: rgba(230, 147, 10, 0.18); + --color-priority-high-bg: rgba(212, 81, 30, 0.18); + --color-priority-urgent-bg: rgba(229, 83, 75, 0.18); + + /* Overlay */ + --color-overlay: rgba(0, 0, 0, 0.6); + --color-overlay-light: rgba(0, 0, 0, 0.35); + + /* Schatten stärker im Dark Mode */ + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.45); } } diff --git a/public/sw.js b/public/sw.js index 74d1dbd..884c65c 100644 --- a/public/sw.js +++ b/public/sw.js @@ -12,9 +12,9 @@ * API: Immer Netzwerk (kein Caching von Nutzerdaten) */ -const SHELL_CACHE = 'oikos-shell-v9'; -const PAGES_CACHE = 'oikos-pages-v9'; -const ASSETS_CACHE = 'oikos-assets-v9'; +const SHELL_CACHE = 'oikos-shell-v10'; +const PAGES_CACHE = 'oikos-pages-v10'; +const ASSETS_CACHE = 'oikos-assets-v10'; const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, ASSETS_CACHE]; // App-Shell: sofort benötigt für ersten Render diff --git a/server/index.js b/server/index.js index 6d98bcf..ec1b3f9 100644 --- a/server/index.js +++ b/server/index.js @@ -30,6 +30,7 @@ const PORT = process.env.PORT || 3000; // -------------------------------------------------------- // Security-Middleware // -------------------------------------------------------- +const isSecure = process.env.SESSION_SECURE !== 'false'; app.use(helmet({ contentSecurityPolicy: { directives: { @@ -45,13 +46,16 @@ app.use(helmet({ fontSrc: ["'self'"], objectSrc: ["'none'"], frameSrc: ["'none'"], + // upgrade-insecure-requests nur mit HTTPS aktivieren + upgradeInsecureRequests: isSecure ? [] : null, }, }, - hsts: { + // HSTS nur mit HTTPS aktivieren + hsts: isSecure ? { maxAge: 31536000, includeSubDomains: true, preload: true, - }, + } : false, })); // Trust Proxy für korrekte IP hinter Nginx