Kundenansicht neu: Wasser-Leiste breiter mit Wörtern, Unterseiten im Vollbild

Wunsch vom 29.10. nach dem ersten Blick am iPhone:
- Die Leiste nutzt die Breite (12 px Rand, höchstens 480 px), Symbol über
  dem Wort, und steht ganz über dem Wisch-Balken des iPhones
  (Safe-Area plus 6 px statt minus 10 px).
- Unterseiten öffnen sich ganz und werden mit Unschärfe eingeblendet statt
  hochzufahren. Oben links die Überschrift, oben rechts „Zurück“ als
  Glasknopf, der beim Scrollen stehen bleibt (neuer Platz
  #kn-blatt-zurueck).
- Reiter einer Funktion stehen unten in derselben Wasser-Leiste (Art
  'reiter', Kurzwörter, kleinere Schrift wenn es eng wird), ohne Zurück.
  Ohne Reiter bleibt unten nichts. Die alte Leiste gibt beim Neuaufbau
  ihren WebGL-Kontext frei.

Glas und Unschärfe stehen weiter allein in wasser-leiste.css; huelle.css
bleibt ohne Glas. Desktop unverändert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-29 18:09:42 +00:00
1 parent c997703251
commit 1f350ca432
10 files changed
+147 -95

No files matched your search

+21 -4
View File
@@ -90,6 +90,20 @@ test('Bauen: vier Reiter mit Wort, Symbol-Platz und Szene, Auswahl, Zähler am S
} finally { fx.restore() }
})
test('Art reiter: nur Wörter (Kurzwort sichtbar, volles Wort vorgelesen), keine Szene, kein Zähler; beende räumt auf', async () => {
const fx = installFakeDom()
try {
const gew = []
const w = baueWasserLeiste({ eintraege: [{ key: 'termine', label: 'Termine' }, { key: 'team', label: 'Team & Plätze', kurz: 'Team' }], aktiv: 'termine', art: 'reiter', ariaLabel: 'Reiter', bei: (k) => gew.push(k) })
const tabs = fx.alle(w, '[role=tab]')
assert.deepEqual([w.getAttribute('data-art'), w.getAttribute('aria-label'), fx.alle(w, '.kn-wl-ico').length], ['reiter', 'Reiter', 0])
assert.deepEqual(tabs.map((t) => [t.textContent, t.getAttribute('aria-label')]), [['Termine', 'Termine'], ['Team', 'Team & Plätze']])
await fx.klick(tabs[1]); assert.deepEqual([gew, tabs[1].getAttribute('aria-selected')], [['team'], 'true'])
w.setzeAnzeige('team', { zahl: 2 }); assert.equal(fx.finde(w, '.kn-zahl'), null)
w.beende(); w.beende() // zweimal ist kein Fehler
} finally { fx.restore() }
})
test('Symbole und Szenen: für jeden Bereich der Leiste ein Symbol und eine eigene Szene', () => {
for (const { key } of WOERTER) { assert.ok(SYMBOLE[key]?.length > 0, key); assert.ok(SZENEN[key], key) }
assert.equal(new Set(WOERTER.map((w) => SZENEN[w.key])).size, WOERTER.length)
@@ -100,15 +114,18 @@ test('wasser-leiste.css: Pille wie die Vorlage, am Handy schwebend über dem Inh
const css = lies('wasser-leiste.css'); const handy = handyTeil(css)
assert.match(css, /\.kn-wl-glas\s*\{[^}]*backdrop-filter:\s*blur\(10px\) saturate\(1\.2\) brightness\(1\.08\)/s)
assert.match(css, /\.kn-wl\[data-gl="off"\] \.kn-wl-tropfen\s*\{\s*display:\s*block/)
assert.match(css, /@media \(max-width: 520px\)\s*\{[^@]*\.kn-wl-label\s*\{[^}]*clip:\s*rect\(0 0 0 0\)/s) // schmal nur Symbole, Wort bleibt für Screenreader
assert.doesNotMatch(css, /max-width: 520px/) // die Wörter stehen immer da (Wunsch 29.10.)
assert.match(css, /\.kn-wl\[data-art="bereiche"\] \.kn-wl-item\s*\{[^}]*flex:\s*1 1 0;[^}]*flex-direction:\s*column/) // Symbol über dem Wort, gleich breit
assert.match(css, /\.kn-wl\[data-art="reiter"\]\[data-eng\] \.kn-wl-item\s*\{[^}]*font-size:\s*12px/)
for (const fx of ['pop', 'plane', 'drop', 'spin', 'glow', 'wave']) assert.match(css, new RegExp(`\\.kn-wl-ico\\[data-fx="${fx}"\\] svg\\s*\\{[^}]*animation:`))
assert.ok(handy.startsWith(HANDY))
assert.match(handy, /\.kn\s*\{[^}]*--kn-wl-luft:\s*max\(10px, calc\(env\(safe-area-inset-bottom\) - 10px\)\)[^}]*--kn-unten:\s*var\(--kn-wl-zone\)/s)
assert.match(handy, /\.kn\s*\{[^}]*--kn-wl-luft:\s*max\(14px, calc\(env\(safe-area-inset-bottom\) \+ 6px\)\)[^}]*--kn-unten:\s*var\(--kn-wl-zone\)/s) // ganz über dem Wisch-Balken des iPhones
assert.match(handy, /\.kn\.kn-hat-streifen\s*\{\s*--kn-unten:\s*calc\(var\(--kn-wl-zone\) \+ 44px\)/)
assert.ok(handy.indexOf('.kn.kn-tastatur') > handy.indexOf('.kn.kn-hat-streifen')) // Tastatur schlägt den Streifen
assert.match(handy, /\.kn-leiste\s*\{[^}]*background:\s*none[^}]*pointer-events:\s*none/s)
assert.match(handy, /\.kn-blatt\s*\{\s*bottom:\s*0;\s*padding-bottom:\s*calc\(var\(--kn-unten\) \+ 24px\)/)
assert.match(handy, /#kn-zeile-etage\s*\{[^}]*border-radius:\s*999px[^}]*backdrop-filter/s)
assert.match(handy, /\.kn-leiste \.kn-rw\s*\{[^}]*right:\s*12px;[^}]*left:\s*12px;[^}]*max-width:\s*480px/s) // breit: Rand 12 px, höchstens 480 px
assert.match(handy, /\.kn-leiste \.kn-wl\s*\{\s*display:\s*flex;\s*width:\s*100%/)
assert.match(handy, /\.kn-leiste \.kn-rw\s*\{[^}]*pointer-events:\s*none/s); assert.match(handy, /\.kn-leiste \.kn-rw > \*\s*\{\s*pointer-events:\s*auto/)
assert.match(css, /@media \(prefers-reduced-motion: reduce\)\s*\{[^@]*animation:\s*none !important/s)
// huelle.css bleibt unverändert die Grundlage (Desktop und Etage-Logik)
assert.match(lies('huelle.css'), /\.kn-leiste\s*\{[^}]*border-top:\s*1px solid var\(--kn-line\)/)