Heatmap in Geraeterahmen (Computer/Tablet + Handy) und Keks statt "Datenschutz-Einstellungen"

Heatmap im Portal (Funktionen -> Analyse): die echte Website laeuft jetzt in realistischen
Geraeterahmen, gross MacBook oder iPad (Umschalter oben), daneben das iPhone, jeweils in echter
Breite (1512 / 820 / 402 px), verkleinert und im Geraet scrollbar, mit den Klicks der
passenden Geraeteklasse als Overlay. MacBook und iPhone stammen aus den Rahmen der neuen
Website (device-mockups), das iPad ist im gleichen Stil nachgebaut (public/geraete.css/.js).

- heatmap-link: embed: true haengt __wk_embed=1 an; w.js reicht das als data-embed an h.js.
- h.js eingebettet (nur in einem Rahmen): keine Leiste, Links und Formulare gesperrt (kein
  Seitenwechsel, also keine Zaehlung und kein Banner), Scrollleisten aus, Klickzahl per
  postMessage nur an die Herkunft von h.js (project.webklar.com).
- iframe mit Sandbox allow-scripts allow-same-origin (keine Formulare, Pop-ups, keine
  Navigation des Portals); Laden erst, wenn die Heatmap in Sicht kommt. Laesst sich eine
  Website nicht einbetten, kommt nach 20 s ein Hinweis mit "In neuem Tab oeffnen" (alter Weg).

Banner-Skript: der Einstellungsknopf nach der Wahl ist jetzt ein angeknabberter Keks unten
rechts (Inline-SVG, 44 px). Er dreht sich langsam, haelt beim Darueberfahren/Fokus an und
zeigt dann "Datenschutz-Einstellungen" (auch sein Name fuer Screenreader, passend zum
Bannertext); bei reduzierter Bewegung dreht er sich nicht.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-02 15:09:17 +00:00
1 parent ff7fe35c06
commit 0a113c2983
14 files changed
+1658 -91

No files matched your search

+8
View File
@@ -188,6 +188,14 @@ test('Heatmap-Link: Prüfung, URL, Token', async () => {
assert.equal(v.siteKey, key)
assert.equal(v.path, '/preise')
assert.equal(v.vp, 'm')
assert.equal(url.searchParams.get('__wk_embed'), null, 'ohne embed kein Einbettungsparameter')
// embed: fuer den Geraeterahmen im Portal
const em = await link({ path: '/preise', vp: 'd', embed: true })
assert.equal(em.status, 200)
const eu = new URL(em.body.url)
assert.equal(eu.searchParams.get('__wk_embed'), '1')
assert.equal(verifyHeatToken(eu.searchParams.get('__wk_heat'), config.analyse.heatSecret, Date.now()).vp, 'd')
assert.equal(new URL((await link({ path: '/', vp: 'd', embed: 'ja' })).body.url).searchParams.get('__wk_embed'), null, 'nur true zaehlt')
// ohne Live-Domain: Subdomain
await post(`${A}/activate`, { projectId: 'p3', consent: consent() }, { cookie })
const sub = await post(`${A}/heatmap-link`, { projectId: 'p3', path: '/', vp: 't' }, { cookie })