From 4fd843318fa6a4acc3808026daa28f5303649c88 Mon Sep 17 00:00:00 2001 From: Thomas Hackner Date: Tue, 14 Jul 2026 00:34:49 +0200 Subject: [PATCH] Header/Navigation-Styling ueberarbeitet Co-Authored-By: Claude --- public/css/styles.css | 88 ++++++++++++++++++++++++++++----------- views/partials/header.ejs | 51 ++++++++++++----------- 2 files changed, 90 insertions(+), 49 deletions(-) diff --git a/public/css/styles.css b/public/css/styles.css index f1a7826..0a30b46 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -10,12 +10,37 @@ /* ============================================ Top navigation (views/partials/header.ejs) ============================================ - The bar keeps its deep navy in both themes: it frames the light content area - like the chrome of a tool, and it is the one surface that never changes as you - move through the app. Everything in it is quiet — slate on navy — except the - active item, which is the single accent: a thin, glowing sky rail. That rail is - the whole point, since the previous header had no active state at all. - The dropdown panels follow the theme (white / slate) so they read as content. */ + The bar carries no surface of its own — it takes the page background and is + held apart from the content only by a hairline (and, once scrolled, the + faintest shadow). Everything in it is quiet; the single accent is the active + item, marked by a thin rail on the bottom edge. That rail is the whole point, + since the header used to have no active state at all. + + --nav-bg mirrors the page background per theme. It is what the focus rings and + the badge ring cut themselves out against — without it they would need a + hard-coded colour and would break in one of the two themes. */ +:root { --nav-bg: #f9fafb; } /* gray-50, matches body */ +.dark { --nav-bg: #111827; } /* gray-900, matches body */ + +.nav-brand:focus-visible, +.nav-item:focus-visible, +.icon-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--nav-bg), 0 0 0 4px rgba(37, 99, 235, 0.6); +} +.dark .nav-brand:focus-visible, +.dark .nav-item:focus-visible, +.dark .icon-btn:focus-visible { + box-shadow: 0 0 0 2px var(--nav-bg), 0 0 0 4px rgba(56, 189, 248, 0.7); +} + +/* Hairline separation only once content actually slides underneath. */ +#siteHeader.is-scrolled { + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04); +} +.dark #siteHeader.is-scrolled { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); +} .nav-item { position: relative; @@ -27,22 +52,23 @@ font-size: 0.875rem; font-weight: 500; letter-spacing: -0.01em; - color: rgb(148 163 184); /* slate-400 */ + color: rgb(71 85 105); /* slate-600 */ white-space: nowrap; transition: color 150ms ease, background-color 150ms ease; } button.nav-item, .icon-btn { cursor: pointer; } .nav-item:hover { + color: rgb(15 23 42); /* slate-900 */ + background-color: rgba(15, 23, 42, 0.05); +} +.nav-item.is-active { color: rgb(15 23 42); } + +.dark .nav-item { color: rgb(148 163 184); } /* slate-400 */ +.dark .nav-item:hover { color: #fff; background-color: rgba(255, 255, 255, 0.06); } -.nav-item.is-active { - color: #fff; -} -.nav-item:focus-visible { - outline: none; - box-shadow: 0 0 0 2px #0b1220, 0 0 0 4px rgba(56, 189, 248, 0.7); -} +.dark .nav-item.is-active { color: #fff; } /* The rail: sits on the header's bottom edge, lights up for the active section and previews faintly on hover. */ @@ -58,14 +84,18 @@ button.nav-item, .icon-btn { cursor: pointer; } transition: transform 200ms ease, background-color 200ms ease; } .nav-item:hover .nav-rail { - background-color: rgba(148, 163, 184, 0.45); + background-color: rgba(100, 116, 139, 0.35); transform: scaleX(1); } .nav-item.is-active .nav-rail { - background-color: rgb(56 189 248); /* sky-400 */ - box-shadow: 0 0 12px rgba(56, 189, 248, 0.8); + background-color: rgb(37 99 235); /* blue-600, the app's action colour */ transform: scaleX(1); } +.dark .nav-item:hover .nav-rail { background-color: rgba(148, 163, 184, 0.4); } +.dark .nav-item.is-active .nav-rail { + background-color: rgb(56 189 248); /* sky-400 reads better on the dark page */ + box-shadow: 0 0 10px rgba(56, 189, 248, 0.6); +} /* Counters shown inline next to a nav label. `:not(.hidden)` is deliberate: a bare `display: inline-flex` would fight @@ -97,17 +127,18 @@ button.nav-item, .icon-btn { cursor: pointer; } width: 2.25rem; height: 2.25rem; border-radius: 0.5rem; - color: rgb(148 163 184); + color: rgb(71 85 105); /* slate-600 */ transition: color 150ms ease, background-color 150ms ease; } .icon-btn:hover { + color: rgb(15 23 42); + background-color: rgba(15, 23, 42, 0.05); +} +.dark .icon-btn { color: rgb(148 163 184); } +.dark .icon-btn:hover { color: #fff; background-color: rgba(255, 255, 255, 0.08); } -.icon-btn:focus-visible { - outline: none; - box-shadow: 0 0 0 2px #0b1220, 0 0 0 4px rgba(56, 189, 248, 0.7); -} /* Count badge floating on an icon button. Same `:not(.hidden)` reason as above. */ .dot-badge:not(.hidden) { @@ -127,7 +158,7 @@ button.nav-item, .icon-btn { cursor: pointer; } font-weight: 700; line-height: 1; color: #fff; - box-shadow: 0 0 0 2px #0b1220; + box-shadow: 0 0 0 2px var(--nav-bg); /* cut out against the bar in both themes */ } .dot-badge--red { background-color: rgb(244 63 94); } @@ -216,11 +247,18 @@ nav .dd-panel { right: auto; left: 0; } border-radius: 0.5rem; font-size: 0.9375rem; font-weight: 500; - color: rgb(148 163 184); + color: rgb(71 85 105); /* slate-600 */ transition: background-color 150ms ease, color 150ms ease; } -.m-item:hover { background-color: rgba(255, 255, 255, 0.06); color: #fff; } +.m-item:hover { background-color: rgba(15, 23, 42, 0.05); color: rgb(15 23 42); } .m-item.is-active { + color: rgb(29 78 216); /* blue-700 */ + background-color: rgb(239 246 255); /* blue-50 */ + box-shadow: inset 2px 0 0 rgb(37 99 235); +} +.dark .m-item { color: rgb(148 163 184); } +.dark .m-item:hover { background-color: rgba(255, 255, 255, 0.06); color: #fff; } +.dark .m-item.is-active { color: #fff; background-color: rgba(56, 189, 248, 0.12); box-shadow: inset 2px 0 0 rgb(56 189 248); diff --git a/views/partials/header.ejs b/views/partials/header.ejs index 7e68c56..1b25020 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -24,30 +24,31 @@ var angemeldet = (typeof user !== 'undefined' && user); var initiale = angemeldet ? String(user.username || '?').charAt(0).toUpperCase() : '?'; %> +