Kundenansicht neu: Glas-Logo auf der Anmeldung und Startbild der App

- public/neu/glas-logo.js: das Bauteil „liquid-glass-logo-reveal“ (21st.dev, Shader und
  Zeitleiste aus zanwei/glass-icon-reveal, MIT, Hinweis im Kopf) in reines JavaScript übertragen:
  WEBklar-Zeichen als Zeichenfläche (Stern und Punkt ausgestanzt), weiß, keine Schleife,
  Textur 512 px (auf dem Handy ~3× schneller bereit), Hintergrund läuft zum Rand aus,
  kein Rauschen auf reinem Weiß, ohne WebGL2 das ruhende Logo als CSS-Maske.
- Anmeldung (Handy und PC): Glas-Logo statt App-Icon, der Name erscheint, sobald das Glas
  losgelassen hat; ein Klick spielt es erneut; reduzierte Bewegung zeigt das ruhende Logo.
- Installierte App: startbild.js im Kopf von Übersicht und Anmeldung deckt beim ersten Öffnen
  je App-Start weiß ab, spielt das Glas-Logo (Tempo 1,25) und blendet dann weich aus; ein Tipp
  überspringt, spätestens nach 6,5 s ist es weg. Danach ruht das Logo der Anmeldung.
- Tests: knGlasLogo (Zeitleiste, Glasbreite < 1,3 Kacheln, MIT-Hinweis, Startbild in der VM),
  Seitenprüfungen der Anmeldung und der Übersicht kennen die neuen Skripte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-29 17:51:44 +00:00
1 parent 1725aaa91c
commit c997703251
9 files changed
+1221 -10

No files matched your search

+20 -2
View File
@@ -8,11 +8,15 @@ const PAARE = ['login', 'kuendigen', 'widerrufen', 'auftrag', 'offline'].map((n)
const koerper = (html) => html.slice(html.search(/<body/i)) // Metas und Links im Kopf ändern sich bewusst (Stil, theme-color), geprüft werden sie unten
const attribute = (html) => [...koerper(html).matchAll(/\s(name|id|value|type|autocomplete|maxlength|required|min|max|step|inputmode|placeholder|enterkeyhint|data-text|data-label|data-label-for)(?:="([^"]*)")?/g)].map((m) => `${m[1]}=${m[2] ?? ''}`).sort()
const skripte = (html) => [...html.matchAll(/<script[^>]*>[\s\S]*?<\/script>/g)].map((m) => m[0].replace(/\s+/g, ' '))
// Eigene Zusätze der neuen Anmeldung, die die alte Seite nicht hat: Glas-Logo (login-logo.js) und Startbild der App (startbild.js)
const ZUSAETZE = ['<script src="/neu/startbild.js"></script>', '<script type="module" src="/neu/login-logo.js"></script>']
const ohneZusaetze = (html) => ZUSAETZE.reduce((h, z) => h.replace(z, ''), html)
for (const [alt, neu] of PAARE) {
test(`${neu}: gleicher sichtbarer Text, gleiche Felder, gleiche Skripte wie ${alt}`, () => {
// Kündigen und Widerrufen sprechen seit P7 du (Fassung 6, C14): ihr Text steht in test/knOeffentlichDu.test.js; Felder und Skripte werden weiter verglichen
if (!['kuendigen.html', 'widerrufen.html'].includes(alt)) assert.equal(sichtbarerText(lese(neu)), sichtbarerText(lese(alt)))
assert.deepEqual(attribute(lese(neu)), attribute(lese(alt))); assert.deepEqual(skripte(lese(neu)), skripte(lese(alt)))
const n = ohneZusaetze(lese(neu))
if (!['kuendigen.html', 'widerrufen.html'].includes(alt)) assert.equal(sichtbarerText(n), sichtbarerText(lese(alt)))
assert.deepEqual(attribute(n), attribute(lese(alt))); assert.deepEqual(skripte(n), skripte(lese(alt)))
})
test(`${neu}: neuer Stil, hell, ohne Verläufe, Lila und Dunkelmodus`, () => {
const n = lese(neu)
@@ -24,6 +28,20 @@ for (const [alt, neu] of PAARE) {
test('Login: Skript lädt app.js und startet initLoginPage, Statusleiste hell', () => {
const n = lese('neu/oeffentlich/login.html'); assert.match(n, /<script src="\/app\.js"><\/script>/); assert.match(n, /initLoginPage\(\)/); assert.match(n, /apple-mobile-web-app-status-bar-style" content="default"/)
})
test('Login: Glas-Logo statt App-Icon (Handy und PC), Startbild der App als letztes im Kopf, Logo-Skript nach initLoginPage', () => {
const n = lese('neu/oeffentlich/login.html')
assert.match(n, /<script src="\/neu\/startbild\.js"><\/script>\s*<\/head>/)
assert.match(n, /<div class="kn-login-marke">\s*<div class="kn-login-glas"><\/div>\s*<span>WEBklar Kundenbereich<\/span>/)
assert.doesNotMatch(n, /app-icon-192\.png" alt=""/)
assert.match(n, /initLoginPage\(\);\s*<\/script>\s*<script type="module" src="\/neu\/login-logo\.js"><\/script>\s*<\/body>/)
const css = lese('neu/oeffentlich/oeffentlich.css')
assert.doesNotMatch(css, /\.kn-login-marke\s*\{\s*display:\s*none/, 'die Marke steht jetzt auch am PC')
assert.match(css, /\.kn-login-glas\s*\{[^}]*height:\s*220px/); assert.match(css, /\.kn-login-glas\s*\{\s*height:\s*190px;\s*\}/)
// Der Name erscheint erst nach dem Glas, sofort wenn das Logo ruht (nach dem Startbild) oder bei reduzierter Bewegung
assert.match(css, /\.kn-login-marke > span\s*\{[^}]*animation:\s*kn-login-name[^}]*\}/)
assert.match(css, /\.kn-login-marke\.kn-login-ruht > span\s*\{\s*animation:\s*none;\s*\}/)
assert.match(css, /@media \(prefers-reduced-motion: reduce\)\s*\{\s*\.kn-login-marke > span\s*\{\s*animation:\s*none;\s*\}\s*\}/)
})
test('Auftragsseite: Token bleibt geschützt (noindex, kein Referrer), Rückweg, Wurzel und Modul bleiben, hell, jede Klasse hat eine Regel', () => {
const n = lese('neu/oeffentlich/auftrag.html')
assert.match(n, /<meta name="robots" content="noindex, nofollow">/); assert.match(n, /<meta name="referrer" content="no-referrer">/); assert.match(n, /<meta name="color-scheme" content="light">/)