diff --git a/server/pruef-handy.mjs b/server/pruef-handy.mjs index 8378b390..f1641983 100644 --- a/server/pruef-handy.mjs +++ b/server/pruef-handy.mjs @@ -452,7 +452,7 @@ console.log("\n=== Kein Licht ohne Zeiger"); werden, und dann faellt es nur bei einer auf. --------------------------------------------------------------------- */ console.log("\n=== Ring und Uhr stehen mittig (alle Rollen)"); -for (const [breite, hoehe] of [[390, 844], [412, 915]]) { +for (const [breite, hoehe] of [[360, 800], [390, 844], [412, 915]]) { for (const [rolle, code] of [["admin", "CODE-DOGI-0001"], ["spicy", "CODE-SPIC-0001"], ["manager", "CODE-MANA-0001"], ["scout", "CODE-SCOU-0001"], ["creator", "CODE-ANNA-0001"]]) { const kontext = await browser.newContext({ viewport: { width: breite, height: hoehe } }); @@ -480,6 +480,30 @@ for (const [breite, hoehe] of [[390, 844], [412, 915]]) { && Math.abs(m.ring) <= 3 && Math.abs(m.uhr) <= 3 && m.ueber <= 1; ok(gut, `${breite}px ${rolle.padEnd(8)} Ring ${m.ring} px, Uhr ${m.uhr} px von der Mitte` + (m.ueber > 1 ? `, Ueberstand ${m.ueber}` : "")); + + /* ---- UND DIE KOPFLEISTE BLEIBT IN EINER REIHE (09.09.2026) ------ + Filipe, mit Bildschirmfoto: "es soll alles in einer reihe sein." + Auf seinem Bild stand "Abmelden" allein in einer zweiten Zeile. + + Gemessen wird die ZEILENZAHL, nicht die Hoehe: Eine Hoehengrenze + waere wieder eine Zahl von gestern -- sie stimmt, bis jemand die + Polsterung anfasst. Ob zwei Dinge in derselben Zeile stehen, + sieht man an ihrer Oberkante, und das gilt immer. */ + const kopf = await seite.evaluate(() => { + const k = document.querySelector(".kopfleiste"); + const teile = [...k.querySelectorAll(".kopfleiste__rechts > *"), k.querySelector(".marke")] + .filter((e) => e && e.getBoundingClientRect().width > 0); + const kanten = teile.map((e) => Math.round(e.getBoundingClientRect().top)); + return { zeilen: new Set(kanten).size, spanne: Math.max(...kanten) - Math.min(...kanten), + anzahl: teile.length, + ueber: document.documentElement.scrollWidth - document.documentElement.clientWidth }; + }); + /* 12 px Nachsicht: Ein Knopf von 36 px und ein Bild von 28 px sind + mittig ausgerichtet und haben deshalb verschiedene Oberkanten -- + das ist dieselbe Zeile. Eine zweite Zeile liegt 40 px tiefer. */ + ok(kopf.anzahl >= 3 && kopf.spanne <= 12 && kopf.ueber <= 1, + `${breite}px ${rolle.padEnd(8)} Kopfleiste: ${kopf.anzahl} Teile in einer Reihe ` + + `(Spanne ${kopf.spanne} px)`); await kontext.close(); } } diff --git a/workspace/assets/css/start.css b/workspace/assets/css/start.css index baee7dc7..0c140021 100644 --- a/workspace/assets/css/start.css +++ b/workspace/assets/css/start.css @@ -3743,7 +3743,18 @@ body[data-fremde-sicht="ja"]::after { Eine zweite Zeile auf einem 320-Pixel-Schirm kostet 44 Pixel Hoehe. Das ist der guenstigste Preis, der hier zu haben ist -- und er faellt nur auf Geraeten an, die wirklich so schmal sind. */ -@media (max-width: 380px) { +/* DIESE SCHWELLE IST AM 09.09.2026 VON 380 AUF 300 GEFALLEN. + + Sie zwang Marke und Bedienelemente ausdruecklich in ZWEI Zeilen -- + und war richtig, solange die Bedienelemente 243 bis 393 px brauchten. + Seit sie gleich grosse Zeichen sind, brauchen sie 136 bis 238; bei + 360 px stehen 334 zur Verfuegung. Die Regel machte ab da zwei Zeilen + aus etwas, das in eine passt. + + 300 px und nicht ganz weg: Darunter gibt es Geraete, auf denen es + wirklich nicht reicht, und dort ist die zweite Zeile besser als ein + Ueberstand. Gemessen bei 360, 390, 412 und 430 -- alle einzeilig. */ +@media (max-width: 300px) { .kopfleiste { flex-wrap: wrap; row-gap: 8px; } /* Die Marke nimmt die erste Zeile ganz ein, die Bedienelemente ruecken geschlossen in die zweite. Ohne das brechen sie mitten @@ -7284,3 +7295,98 @@ body.start .k-leiste .feldschild::after { font-size: .85rem; color: var(--text-still); border: 1px dashed rgba(150, 186, 220, .22); } + +/* ===================================================================== + DIE KOPFLEISTE BLEIBT AUF DEM HANDY IN EINER REIHE (09.09.2026) + + Filipe, mit Bildschirmfoto: "ich will dass es überall perfektionniert + wird. ich will dass du das richtig geil machst es soll alles in einer + reihe sein." + + GEMESSEN, BEVOR ETWAS GEÄNDERT WURDE (390 px, alle fünf Rollen): + + verfügbar für die rechte Gruppe 275 px + gebraucht als DogFather 393 px (Sicht 150 · Teilen 38 · + Chat 37 · Suche 47 · Bild 28 · + Abmelden 93) + gebraucht als Scout/Manager 243 px + gebraucht als Creator 205 px + + Der Umbruch war also die richtige Antwort auf ein echtes + Platzproblem -- `flex-wrap` misst und rechnet nicht, das bleibt auch + so. Geändert wird der PLATZBEDARF, nicht die Reaktion darauf. Eine + Schwelle, ab der nicht mehr umgebrochen wird, wäre wieder eine + Rechnung von gestern: Beim nächsten Knopf stünde alles übereinander. + + Was schrumpft, und was nicht: + * Die drei Zeichen-Knöpfe werden gleich groß (36 px) -- vorher + waren sie 38, 37 und 47, was nebeneinander unruhig aussah. + * "Abmelden" wird zum Zeichen (93 -> 36). Das Wort bleibt für + Vorleseprogramme im aria-label. + * Der Sicht-Umschalter zeigt nur noch das Auge -- ABER NUR, + solange die eigene Sicht läuft. Bei einer fremden Sicht behält + er den Namen, und dann darf die Leiste auch umbrechen: Dass man + fremde Zahlen ansieht, ist wichtiger als eine gerade Zeile. + + Rechnung danach bei 360 px, dem schmalsten Gerät: 36+36+36+28+36 + = 172 plus vier Abstände zu 6 = 196, verfügbar 249. + ===================================================================== */ +@media (max-width: 560px) { + .kopfleiste__rechts { gap: 6px; } + + /* Alle Zeichen-Knöpfe auf ein Maß. 36 px ist knapp über der Grenze, + ab der man danebentippt, und lässt vier davon nebeneinander. */ + .kopfleiste__rechts > .teilen, + .kopfleiste__rechts > .chat-knopf, + .kopfleiste__rechts > .suche-knopf, + .kopfleiste__rechts > .abmelden { + width: 36px; min-width: 36px; height: 36px; min-height: 36px; + padding: 0; gap: 0; + display: inline-grid; place-items: center; + } + .abmelden__text { display: none; } + .abmelden__zeichen { + width: 18px; height: 18px; + fill: none; stroke: currentColor; stroke-width: 1.8; + stroke-linecap: round; stroke-linejoin: round; + } + + /* Der Umschalter wird ein Zeichen-Knopf wie die anderen -- aber NUR, + solange die eigene Sicht laeuft. Bei einer fremden behaelt er den + Namen, und dann darf die Leiste auch umbrechen: Dass man fremde + Zahlen ansieht, ist wichtiger als eine gerade Zeile. + + Der Knopf liegt unsichtbar UEBER dem Auge, statt daneben. Das + bewahrt die Bedienbarkeit (die ganze Flaeche ist das Ziel) und den + Fokusring, kostet aber keine Breite. */ + .sicht:not([data-fremd="ja"]) { + position: relative; + width: 36px; min-width: 36px; height: 36px; + padding: 0; gap: 0; justify-content: center; + } + .sicht:not([data-fremd="ja"]) .sicht__auge { position: relative; z-index: 0; } + .sicht:not([data-fremd="ja"]) .wahl2 { position: absolute; inset: 0; z-index: 1; } + .sicht:not([data-fremd="ja"]) .wahl2__knopf { + width: 100%; height: 100%; padding: 0; + background: none; border-color: transparent; + } + .sicht:not([data-fremd="ja"]) .wahl2__knopf svg { display: none; } + /* DER KNOPF WIRD GEKLAMMERT, NICHT SEIN KASTEN. + + Erster Anlauf war `max-width: 30px` am umgebenden .wahl2. Gemessen + stand danach genau das da: berechnete Hoechstbreite 30 px -- + tatsaechliche Breite 104. Der Knopf DARIN behielt seine Groesse und + schob den Kasten wieder auf. Wer nur den Rahmen begrenzt, begrenzt + nichts; die Breite kommt vom Inhalt. */ + .sicht:not([data-fremd="ja"]) .wahl2, + .sicht:not([data-fremd="ja"]) .wahl2__knopf { max-width: none; min-width: 0; } + .sicht:not([data-fremd="ja"]) .wahl2__text { display: none; } +} + +/* Am Rechner bleibt das Wort -- dort ist Platz, und ein Wort ist + eindeutiger als ein Bild. Das Zeichen steht davor. */ +.abmelden__zeichen { + width: 16px; height: 16px; flex: none; + fill: none; stroke: currentColor; stroke-width: 1.8; + stroke-linecap: round; stroke-linejoin: round; +} diff --git a/workspace/assets/js/kopf.js b/workspace/assets/js/kopf.js index 6167aa69..65977dad 100644 --- a/workspace/assets/js/kopf.js +++ b/workspace/assets/js/kopf.js @@ -594,6 +594,46 @@ } const abmelden = document.getElementById('abmelden'); + /* EIN ZEICHEN NEBEN DAS WORT (09.09.2026). + ------------------------------------------------------------------ + Filipe, mit einem Bildschirmfoto der Kopfleiste vom Handy: "ich + will dass es ueberall perfektionniert wird ... es soll alles in + einer reihe sein." + + Auf seinem Bild stand "Abmelden" allein in einer zweiten Zeile. + Nachgemessen bei 390 px: Die rechte Gruppe hat dort 275 px Platz + und braucht 393 -- der Umbruch war die richtige Reaktion auf ein + Platzproblem, nicht der Fehler. Der Fehler war der Platzbedarf. + + Das Wort allein ist 93 px breit. Als Zeichen sind es 36. Es + verschwindet aber nur auf schmalen Bildschirmen (siehe start.css); + am Rechner steht weiterhin das Wort da, weil dort Platz ist und + ein Wort immer eindeutiger ist als ein Bild. + + Der Aufbau steht HIER und nicht in neunzehn HTML-Dateien: kopf.js + laeuft auf jeder Seite, und eine Aenderung an neunzehn Stellen ist + achtzehn Gelegenheiten, eine zu vergessen. */ + if (abmelden && !abmelden.dataset.zeichenDa) { + abmelden.dataset.zeichenDa = 'ja'; + const wort = (abmelden.textContent || 'Abmelden').trim(); + abmelden.textContent = ''; + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('class', 'abmelden__zeichen'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('aria-hidden', 'true'); + const pfad = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + /* Tuer mit Pfeil hinaus -- das gebraeuchlichste Zeichen dafuer. */ + pfad.setAttribute('d', 'M15 4.5H6.5a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2H15M12.5 12h7m0 0-3-3m3 3-3 3'); + svg.append(pfad); + const text = document.createElement('span'); + text.className = 'abmelden__text'; + text.textContent = wort; + abmelden.append(svg, text); + /* Die Beschriftung bleibt auch dann, wenn das Wort nicht zu sehen + ist -- sonst waere der Knopf fuer ein Vorleseprogramm leer. */ + abmelden.setAttribute('aria-label', wort); + if (!abmelden.title) abmelden.title = wort; + } if (abmelden) { abmelden.addEventListener('click', async () => { abmelden.disabled = true; diff --git a/workspace/aufgaben.html b/workspace/aufgaben.html index 9f78cdd1..c640164e 100644 --- a/workspace/aufgaben.html +++ b/workspace/aufgaben.html @@ -14,10 +14,10 @@ - - - - + + + +
@@ -231,10 +231,10 @@ - - - - - + + + + +