From 52a48cacb9c615626dcd2e4a48f652f0d062995b Mon Sep 17 00:00:00 2001 From: MetalCircle Codex Bot Date: Tue, 15 Sep 2026 12:34:26 +0200 Subject: [PATCH] Refine shared header language selector --- app/static/css/language.css | 98 +++++++++++++++++++++-------- app/static/css/style.css | 2 - app/static/js/header-controls.js | 24 +++++++ app/templates/_language_switch.html | 18 ++++-- app/templates/_user_menu.html | 2 +- app/templates/concert_detail.html | 2 +- app/templates/index.html | 27 +++++--- app/tests/test_i18n.py | 4 +- 8 files changed, 135 insertions(+), 42 deletions(-) create mode 100644 app/static/js/header-controls.js diff --git a/app/static/css/language.css b/app/static/css/language.css index 9d515de..f79c7d0 100644 --- a/app/static/css/language.css +++ b/app/static/css/language.css @@ -1,32 +1,80 @@ -/* Persistent language choice, also available before login. */ -.language-switch { +/* Shared language/account controls, including pages shown before login. */ +.header-controls { + position: relative; + z-index: 2000; + display: flex; + flex-wrap: nowrap; + align-items: center; + justify-content: flex-end; + gap: 6px; + min-width: 0; + max-width: 100%; + margin-left: auto; +} +.language-switch { position: relative; flex: 0 0 auto; margin: 0; padding: 0; } +.language-switch > summary { display: flex; align-items: center; justify-content: center; - flex: 0 0 auto; - margin: 0; - padding: 0; + gap: 5px; + min-height: 44px; + padding: 6px 8px; + border: 1px solid transparent; + border-radius: 9px; + color: var(--muted, #57534e); + background: transparent; + font: 500 .85rem/1.2 system-ui, sans-serif; + white-space: nowrap; + cursor: pointer; + list-style: none; } -.language-switch a { - display: inline-flex; - align-items: center; - justify-content: center; - width: 38px; - height: 36px; - border: 1px solid var(--border, #30363d); - border-radius: 8px; - color: var(--text, #222); - font-family: system-ui, sans-serif; - font-size: 1.2rem; - line-height: 1; - text-decoration: none; +.language-switch > summary::-webkit-details-marker { display: none; } +.language-caret { font-size: .7rem; } +.language-switch[open] > summary, +.language-switch > summary:active { color: var(--text, #f5f5f4); background: var(--surface-hover, #1a1111); border-color: var(--border, #3a3030); } +.language-panel { + position: absolute; + top: calc(100% + 6px); + right: 0; + z-index: 10000; + width: 200px; + max-width: calc(100vw - 24px); + padding: 6px; + border: 1px solid var(--border, #3a3030); + border-radius: 10px; + color: var(--text, #f5f5f4); + background: var(--surface, #101010); + box-shadow: 0 14px 35px rgba(0,0,0,.65); } -.language-switch a.active { background: #922525; color: #fff; border-color: #c74747; } -.language-switch a:focus-visible { outline: 2px solid #fff; outline-offset: 3px; } -.language-switch a:hover { background: #7f1d1d; } -.header-controls { display: flex; align-items: center; justify-content: flex-end; gap: 6px; margin-left: auto; } -.header-controls .user-menu { margin-left: 0; } +.language-heading { margin: 0; padding: 8px 10px; font-size: .8rem; color: var(--muted, #a8a29e); } +.language-panel a { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 8px 10px; border-radius: 6px; color: inherit; font: 400 .9rem/1.3 system-ui, sans-serif; text-decoration: none; } +.language-panel a[aria-current] { background: var(--surface-hover, #1a1111); font-weight: 700; } +.language-selected { margin-left: auto; } +.language-switch summary:focus-visible { outline: 2px solid var(--text, #57534e); outline-offset: 2px; } +.language-panel a:focus-visible, +.header-controls .user-menu summary:focus-visible { outline: 2px solid var(--text, #f5f5f4); outline-offset: 2px; } +@media (hover: hover) { + .language-switch > summary:hover { color: var(--text, #f5f5f4); background: var(--surface-hover, #1a1111); border-color: var(--border, #3a3030); } + .language-panel a:hover { background: var(--surface-hover, #1a1111); } +} +/* Both menus anchor to the same right edge, independent of the page header. */ +.header-controls .language-switch, +.header-controls .user-menu { position: static; margin-left: 0; } +.header-controls .user-menu { min-width: 0; } +.header-controls .user-menu summary { display: flex; align-items: center; gap: 5px; min-height: 44px; max-width: 220px; } +.user-menu-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.header-controls .metal-notification { flex-shrink: 0; margin-right: 7px; } +.header-controls .user-menu-panel { position: absolute; top: calc(100% + 6px); right: 0; max-width: calc(100vw - 24px); max-height: calc(100dvh - 90px); overflow-y: auto; } +.header-inner { gap: 8px; } +.header-inner > .logo { min-width: 0; flex-shrink: 1; } +.header-inner > .logo .brand-logo { max-width: 100% !important; } .header-inner > .language-switch { margin-left: auto; } -.header-actions > .header-controls { flex: 0 0 auto; } -.detail-topbar > .header-controls { flex: 0 0 auto; } +.detail-topbar { align-items: center; padding-inline: 12px; } +.detail-topbar > .back-link { min-width: 0; overflow-wrap: anywhere; } +.detail-topbar > .header-controls { flex-shrink: 0; } .registration-header { display: flex; justify-content: flex-end; margin: -36px 0 12px; } +@media (max-width: 600px) { + .header-controls .user-menu summary { max-width: 30vw; padding: 8px; } + .language-switch > summary { padding: 6px; } + .detail-topbar { gap: 8px; } +} diff --git a/app/static/css/style.css b/app/static/css/style.css index f6953da..76cae72 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -232,7 +232,6 @@ main { .logo { max-width: 130px; } .brand-logo { width: 130px !important; max-width: 130px !important; height: 40px !important; max-height: 40px !important; } - .header-controls .user-menu summary { max-width: 34vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } main { padding: 22px 15px 80px; @@ -248,7 +247,6 @@ main { .header-actions { width: 100%; justify-content: flex-start; } .header-actions > a { flex: 1 1 auto; text-align: center; } - .header-actions > .header-controls { margin-left: auto; } } form { max-width: 650px; diff --git a/app/static/js/header-controls.js b/app/static/js/header-controls.js new file mode 100644 index 0000000..7e5e2a3 --- /dev/null +++ b/app/static/js/header-controls.js @@ -0,0 +1,24 @@ +/* Native details/summary provides keyboard activation and expanded state. */ +(() => { + const selector = '.language-switch, .header-controls .user-menu'; + document.addEventListener('toggle', event => { + if (!event.target.matches(selector) || !event.target.open) return; + document.querySelectorAll(selector).forEach(menu => { + if (menu !== event.target) menu.open = false; + }); + }, true); + document.addEventListener('click', event => { + document.querySelectorAll(selector).forEach(menu => { + if (menu.open && !menu.contains(event.target)) menu.open = false; + }); + }); + document.addEventListener('keydown', event => { + if (event.key !== 'Escape') return; + document.querySelectorAll(selector).forEach(menu => { + if (!menu.open) return; + menu.open = false; + menu.querySelector('summary').focus(); + event.preventDefault(); + }); + }); +})(); diff --git a/app/templates/_language_switch.html b/app/templates/_language_switch.html index 343b99c..3e2995e 100644 --- a/app/templates/_language_switch.html +++ b/app/templates/_language_switch.html @@ -1,4 +1,14 @@ - +
+ + {{ language() | upper }} + + +
+ diff --git a/app/templates/_user_menu.html b/app/templates/_user_menu.html index 9295aae..155de46 100644 --- a/app/templates/_user_menu.html +++ b/app/templates/_user_menu.html @@ -1,5 +1,5 @@
- ☠ {{ user.display_name }}{% if user.notification_count %} 🀘{{ user.notification_count }}{% endif %} + {{ user.display_name }}{% if user.notification_count %} 🀘{{ user.notification_count }}{% endif %}
{{ _('Mein Profil') }} {{ _('Nachrichten') }}{% if user.notification_count %} 🀘{{ user.notification_count }}{% endif %} diff --git a/app/templates/concert_detail.html b/app/templates/concert_detail.html index 1301004..d205f3e 100644 --- a/app/templates/concert_detail.html +++ b/app/templates/concert_detail.html @@ -19,7 +19,7 @@ >