diff --git a/assets/css/app.css b/assets/css/app.css index 55e4e4a..4aaf5f2 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -18,7 +18,7 @@ --radius-md: 18px; --radius-sm: 14px; --panel-blur: 28px; - --font-ui: "SF Pro Display", "Avenir Next", "Segoe UI Variable", "Helvetica Neue", system-ui, sans-serif; + --font-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif; --track-accent: rgba(139, 228, 255, 0.34); --track-surface: rgba(255, 255, 255, 0.08); --track-glow: rgba(139, 228, 255, 0.18); @@ -1017,13 +1017,12 @@ body.page-dashboard .content { .sleep-phase-bar__fill { position: absolute; inset: 0 auto 0 0; - display: flex; width: var(--sleep-actual-width, 0); min-width: 0.18rem; overflow: hidden; border-radius: inherit; - background: transparent; - box-shadow: 0 12px 28px rgba(6, 16, 28, 0.2); + background: linear-gradient(135deg, rgba(90, 188, 242, 0.92), rgba(44, 126, 190, 0.92)); + box-shadow: 0 10px 22px rgba(44, 126, 190, 0.22); z-index: 1; } @@ -1151,6 +1150,28 @@ body.page-dashboard .content { z-index: 2; } +.sleep-phase-legend { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + margin-top: 0.7rem; +} + +.sleep-phase-legend__item { + display: inline-flex; + align-items: center; + min-height: 2rem; + padding: 0.32rem 0.62rem; + border-radius: 999px; + color: rgba(255, 255, 255, 0.92); + font-size: 0.78rem; + gap: 0.25rem; +} + +.sleep-phase-legend__item--deep { background: rgba(44, 82, 180, 0.48); } +.sleep-phase-legend__item--rem { background: rgba(120, 83, 210, 0.48); } +.sleep-phase-legend__item--core { background: rgba(54, 147, 173, 0.48); } + .media-lightbox[hidden] { display: none; } @@ -4697,6 +4718,128 @@ input[type="range"] { } } +.ios-tabbar { + display: none; +} + +@media (max-width: 760px) { + body { + -webkit-tap-highlight-color: transparent; + } + + .ios-tabbar { + position: fixed; + left: 0.75rem; + right: 0.75rem; + bottom: max(0.65rem, env(safe-area-inset-bottom)); + z-index: 120; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 0.25rem; + min-height: 4.8rem; + padding: 0.48rem; + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 1.9rem; + background: rgba(20, 31, 46, 0.72); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.28); + backdrop-filter: blur(24px) saturate(1.35); + -webkit-backdrop-filter: blur(24px) saturate(1.35); + } + + .ios-tabbar a { + display: grid; + place-items: center; + align-content: center; + gap: 0.24rem; + min-height: 3.55rem; + border-radius: 1.35rem; + color: rgba(239, 247, 255, 0.68); + font-size: 0.72rem; + font-weight: 650; + text-decoration: none; + transition: background 160ms ease, color 160ms ease, transform 160ms ease; + } + + .ios-tabbar a.active { + background: rgba(255, 255, 255, 0.14); + color: #fff; + transform: translateY(-1px); + } + + .ios-tabbar__icon { + width: 1.35rem; + height: 1.35rem; + border-radius: 0.45rem; + border: 2px solid currentColor; + opacity: 0.9; + } + + .ios-tabbar a:nth-child(1) .ios-tabbar__icon { border-radius: 50%; } + .ios-tabbar a:nth-child(2) .ios-tabbar__icon { border-radius: 0.35rem; box-shadow: inset 0 -0.35rem 0 currentColor; } + .ios-tabbar a:nth-child(3) .ios-tabbar__icon { border-radius: 0.3rem; box-shadow: inset 0 0 0 0.22rem rgba(255, 255, 255, 0.18); } + .ios-tabbar a:nth-child(4) .ios-tabbar__icon { border-radius: 50%; box-shadow: inset 0 0 0 0.28rem currentColor; } + + body.is-authenticated .content, + body.page-dashboard.is-authenticated .content, + body.page-options.is-authenticated .content { + padding-bottom: calc(6.2rem + env(safe-area-inset-bottom)); + } + + .dashboard-day, + .dashboard-range-view { + width: min(100%, 430px); + padding-top: calc(4.6rem + env(safe-area-inset-top)); + } + + .dashboard-day__hero, + .dashboard-range-view__hero, + .day-summary-card, + .timeline-card, + .range-card, + .range-day-card, + .dashboard-composer { + border-radius: 1.65rem; + box-shadow: 0 14px 40px rgba(4, 18, 31, 0.18); + } + + .timeline-card, + .range-day-card { + padding: 1.05rem; + } + + button, + input, + select, + textarea, + .ghost-button, + .dashboard-switcher a, + .range-moment-list__item, + .signal-pill { + min-height: 44px; + } +} + +@media (max-width: 760px) and (prefers-color-scheme: light) { + .ios-tabbar { + border-color: rgba(120, 146, 172, 0.22); + background: rgba(248, 251, 255, 0.78); + box-shadow: 0 20px 56px rgba(78, 105, 130, 0.18); + } + + .ios-tabbar a { + color: rgba(18, 48, 75, 0.58); + } + + .ios-tabbar a.active { + background: rgba(20, 148, 222, 0.12); + color: #12304b; + } + + .sleep-phase-legend__item { + color: rgba(18, 48, 75, 0.9); + } +} + @media (max-width: 640px) { .shell { padding: 0.8rem; diff --git a/templates/layout.php b/templates/layout.php index 3e3ae52..6bf02c4 100644 --- a/templates/layout.php +++ b/templates/layout.php @@ -125,6 +125,26 @@ $jsVersion = is_file(base_path('assets/js/app.js')) ? (string) filemtime(base_pa + + + diff --git a/templates/pages/dashboard.php b/templates/pages/dashboard.php index 4ef5e41..9f55ca6 100644 --- a/templates/pages/dashboard.php +++ b/templates/pages/dashboard.php @@ -221,31 +221,22 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string { 0): ?> -
-
- 0): ?> - ['Tief', 'deep'], 'rem' => ['REM', 'rem'], 'core' => ['Kern', 'core']] as $phase => [$label, $class]): ?> - - - 0 ? max(0.5, min(100, ($phaseHours / $sleepActualTotal) * 100)) : 0; ?> - - - - - - - 0): ?> - 0 ? max(0.5, min(100, ($sleepUnclassified / $sleepActualTotal) * 100)) : 0; ?> - - Schlaf - - -
+
+ 0): ?> noch bis Skalenende
+ 0): ?> +
+ ['Tief', 'deep'], 'rem' => ['REM', 'rem'], 'core' => ['Kern', 'core']] as $phase => [$label, $class]): ?> + + + + +
+ @@ -589,24 +580,30 @@ $formatBalanceValue = static function (?array $entry) use ($settings): string { $sportType = $eventType === 'sport' ? find_sport_type($settings, (string) ($event['sport_type_id'] ?? '')) : null; $eventValue = (float) ($event['value'] ?? 0); $eventValueText = $eventValue > 0 ? ($eventType === 'sleep' ? format_duration_hours($eventValue) : rtrim(rtrim(number_format($eventValue, 2, ',', '.'), '0'), ',') . ' ' . (string) ($event['unit'] ?? '')) : ''; - $eventTitle = trim((string) ($event['comment'] ?? '')) !== '' ? trim((string) $event['comment']) : day_event_type_label($eventType); - $eventDetail = $eventValueText; + $eventComment = trim((string) ($event['comment'] ?? '')); + if (preg_match('/^\s*-\s*(?:Stimmung|Energie|Stress)\s*:\s*[+-]?\d+\s*$/u', $eventComment) === 1) { + $eventComment = ''; + } + $eventTitle = day_event_type_label($eventType); + $eventDetails = array_values(array_filter([$eventValueText, $eventComment], static fn (string $value): bool => trim($value) !== '')); if ($eventType === 'sport') { $eventTitle = (string) ($sportType['label'] ?? 'Sport'); } - if ($eventType === 'sleep' && trim((string) ($event['comment'] ?? '')) === '') { + if ($eventType === 'sleep') { $eventTitle = 'Schlaf'; + } elseif ($eventType === 'walk') { + $eventTitle = 'Spaziergang'; } ?>
  • - + - +