frontend: Mobile Sprachwahl als Overlay, eingeklappt nur aktive Sprache

- eingeklappt zeigt die Leiste nur noch die aktive Sprache (kein EN-Default,
  kein „…“); ein Tipp klappt die volle Liste als Overlay über die Kopfzeile auf
  (position:absolute, z-index) statt die Zeile zu verbreitern
- nach Auswahl klappt sie wieder auf die gewählte Sprache ein; Tipp daneben
  schließt ebenfalls; applyMobile() startet eingeklappt
- nur body.mobile betroffen; Desktop (DE EN ES FR + „…“, inline) unverändert
- SPEC §9 (Kleiner Bildschirm) und DECISIONS D17 nachgeführt

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-07-21 19:09:01 +02:00
co-authored by Claude Opus 4.8
parent 63cfd82f07
commit fe818b6c2c
3 changed files with 48 additions and 10 deletions
+6 -2
View File
@@ -145,8 +145,12 @@ Fenster-Buttons entfallen, der **Splitter bleibt** (Teilen per Ziehen möglich).
Die Legende wird von der Bereichs-
Umschaltung **entkoppelt** und über einen eigenen Umschalter im Editor-Kopf
gesteuert — das dauerhafte „AGENDA“-Band kostet sonst zu viel Höhe. Die
Sprachleiste zeigt nur Englisch + aktive Sprache + „…“. Voreinstellung ist
**Vollbild** (maximale Fläche). Umgesetzt über die Klasse `body.mobile` (per
Sprachleiste zeigt eingeklappt nur die **aktive** Sprache; ein Tipp klappt die
volle Leiste als **Overlay über die Kopfzeile** auf (rechtsbündig, `z-index`,
verdeckt die übrigen Elemente statt die Zeile umzubrechen), nach der Auswahl —
oder einem Tipp daneben — klappt sie wieder ein. So bleibt die Kopfzeile schmal
und die vollständige Sprachliste dennoch erreichbar (frühere Lösung
„EN + aktive + …“ ersetzt). Voreinstellung ist **Vollbild** (maximale Fläche). Umgesetzt über die Klasse `body.mobile` (per
`matchMedia`), damit CSS und JS denselben Schwellwert teilen; die Vollbild-
Voreinstellung greift nur, wenn noch **keine** gespeicherte Nutzerwahl vorliegt
(eine bewusste Abschaltung bleibt so erhalten, siehe localStorage-Persistenz).
+7 -4
View File
@@ -153,10 +153,13 @@ Min/Normal/Max-Buttons entfallen; geteilt wird weiterhin über den **Splitter**
Umschalter im Editor-Kopf (statt der dauerhaften „AGENDA“-Zeile), damit sie
zugeklappt keinen Platz kostet. Der **Modus-Wähler** zeigt nur das aktive Icon
und schaltet bei jedem Tippen reihum weiter (horizontal → kompakt → vertikal →
…), spart also zwei Drittel der Breite. Die **Sprachwahl** zeigt nur Englisch
und die aktive Sprache plus „…“ (das alle Sprachen aufklappt). Der **Untertitel**
wird auf eine garantiert einzeilige Kurzfassung reduziert. Voreinstellung auf
kleinem Bildschirm ist **Vollbild** (siehe D17).
…), spart also zwei Drittel der Breite. Die **Sprachwahl** zeigt eingeklappt
nur die aktive Sprache; ein Tipp darauf klappt die volle Leiste als **Overlay
über die Kopfzeile** auf (verdeckt die übrigen Bedienelemente, statt die Zeile
zu verbreitern), nach der Auswahl klappt sie wieder auf die gewählte Sprache
ein (Tipp daneben schließt ebenfalls). Der **Untertitel** wird auf eine
garantiert einzeilige Kurzfassung reduziert. Voreinstellung auf kleinem
Bildschirm ist **Vollbild** (siehe D17).
### Günstigster Pfad (Kosten-Hervorhebung)
Ein Umschalter (Icon-Button im Diagramm-Kopf, Voreinstellung **an**, Zustand
+35 -4
View File
@@ -550,11 +550,20 @@
/* Legende: keine dauerhafte Summary-Zeile, dafür der #legendBtn im Kopf */
body.mobile .agenda>summary{display:none}
body.mobile .legendbtn{display:inline-flex}
/* Sprachwahl schlank: nur EN + aktuelle Sprache + „…“; „…“ klappt alle auf */
/* Sprachwahl schlank: eingeklappt nur die aktive Sprache. Ein Tipp klappt die
volle Leiste als Overlay über die Kopfzeile auf (verdeckt fsToggle usw.,
statt die Zeile zu verbreitern); nach der Auswahl klappt sie wieder ein
(JS). Das „…“ entfällt hier — die aktive Sprache ist selbst der Auslöser. */
body.mobile .header-tools{position:relative}
body.mobile .langsel button[data-lang]{display:none}
body.mobile .langsel.expanded button[data-lang]{display:inline-block}
body.mobile .langsel button[data-lang="en"],
body.mobile .langsel button[data-lang].active{display:inline-block}
body.mobile .langmore{display:none}
body.mobile .langsel.expanded{
position:absolute;top:0;right:0;z-index:40;
background:var(--card);
box-shadow:0 6px 18px rgba(36,52,71,.28);
}
body.mobile .langsel.expanded button[data-lang]{display:inline-block}
/* Modus-Wähler kompakt: nur das aktive Icon (ein Drittel Platz), Tippen
schaltet reihum weiter (horizontal → kompakt → vertikal → …, siehe JS). */
body.mobile .seg label{display:none}
@@ -1697,8 +1706,29 @@ langMore.addEventListener('click', () => {
const exp = langsel.classList.toggle('expanded');
langMore.setAttribute('aria-expanded', String(exp));
});
function collapseLangsel(){
langsel.classList.remove('expanded');
langMore.setAttribute('aria-expanded', 'false');
}
document.querySelectorAll('.langsel button[data-lang]').forEach(b => {
b.addEventListener('click', () => applyLang(b.dataset.lang));
b.addEventListener('click', () => {
/* Mobil: eingeklappt ist nur die aktive Sprache sichtbar — ein Tipp klappt
die volle Leiste als Overlay auf; erst im aufgeklappten Zustand wählt ein
Tipp die Sprache und klappt wieder ein. */
if(isMobile() && !langsel.classList.contains('expanded')){
langsel.classList.add('expanded');
langMore.setAttribute('aria-expanded', 'true');
return;
}
applyLang(b.dataset.lang);
if(isMobile()) collapseLangsel();
});
});
/* Mobil: Tipp außerhalb der aufgeklappten Leiste schließt sie wieder. */
document.addEventListener('click', e => {
if(isMobile() && langsel.classList.contains('expanded') && !langsel.contains(e.target)){
collapseLangsel();
}
});
/* ---------- Vollbild: Panels über die ganze Fensterbreite ---------- */
@@ -1797,6 +1827,7 @@ seg.addEventListener('click', e => {
function applyMobile(){
const m = isMobile();
document.body.classList.toggle('mobile', m);
if(m) collapseLangsel(); /* Sprachleiste eingeklappt starten (Overlay-Logik) */
if(!m) return;
/* Nur ein Panel sichtbar: normal/custom passen nicht — auf Diagramm (b). */
if(splitState !== 'a' && splitState !== 'b'){ splitState = 'b'; applySplit(); }