Point the motion check at a block that keeps its entrance
The Kant-IT home page now animates blocks instead of whole sections, so the check waits for the closing call to action rather than the contact section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
2299184401
commit
b634f486e5
2 files changed
+21
-15
No files matched your search
@@ -1,5 +1,11 @@
|
|||||||
# Projektstatus
|
# Projektstatus
|
||||||
|
|
||||||
|
## Kant-IT-Startseite: Einblendungen nach Flatsome-Muster in `var/kantit.db` (14.09.2026)
|
||||||
|
|
||||||
|
**Umgesetzt:** Auf Umuts Wunsch das Animationsmuster der alten Flatsome-Website (www.kant-it.de: fadeInLeft/Right im Hero, bei „Warum“ und Kontakt, gestaffeltes fadeInUp auf Zahlen, Karten und Fußzeilenspalten) in der lokalen Kant-IT-Datenbank nachgebaut: 55 Blöcke der Startseite umgestellt – Sektionen ohne eigene Einblendung, Hero-Text von links, Slider-Spalte von rechts, Kennzahlen und Karten gestaffelt von unten, „Warum“-Texte und -Karten von links, Kundenstimmen von rechts, Kontaktdaten links und Formular rechts – sowie die vier Fußzeilenspalten gestaffelt von unten. Als Entwurf gespeichert und veröffentlicht (Revision `155e2934…`), Sicherung `var/kantit.db.vor-bewegung-*.bak`. Umut kopiert die Datei selbst auf den Server; `web.kant-it.de` läuft bereits auf 1.11.0 (Fassung, `motion.js`, Hover und Einblendungen live geprüft).
|
||||||
|
|
||||||
|
**Geprüft:** `scripts/motion-check.mjs` (Ziel jetzt `#abschluss-cta`, weil die Sektion `#kontakt` keine eigene Einblendung mehr trägt) auf frischer Pilotkopie bei 1600/390 px erfolgreich: 45 Einblendungen gerendert (30 von unten, 11 von links, 4 von rechts), Warten außerhalb des Bildes, einmalige Wiedergabe, Richtungen, Hover, Standardmodus unter „reduce“, `motion-respect`. Keine JavaScript-Fehler in der App-Vorschau.
|
||||||
|
|
||||||
## 1.11.0 – Editor mit einer Seitenleiste nach Flatsome-Vorbild, Bewegung spielt standardmäßig (14.09.2026)
|
## 1.11.0 – Editor mit einer Seitenleiste nach Flatsome-Vorbild, Bewegung spielt standardmäßig (14.09.2026)
|
||||||
|
|
||||||
**Anlass:** Umut fand den Editor mit drei Panels unübersichtlich und kam mit dem Flatsome UX Builder zurecht; außerdem liefen die Einblendungen in seinem Chrome nicht, weil der Browser „Bewegung reduzieren“ meldet, obwohl er das nicht eingestellt hat – andere Websites bewegen sich bei ihm.
|
**Anlass:** Umut fand den Editor mit drei Panels unübersichtlich und kam mit dem Flatsome UX Builder zurecht; außerdem liefen die Einblendungen in seinem Chrome nicht, weil der Browser „Bewegung reduzieren“ meldet, obwohl er das nicht eingestellt hat – andere Websites bewegen sich bei ihm.
|
||||||
|
|||||||
+15
-15
@@ -53,30 +53,30 @@ try {
|
|||||||
const hovered = await hoverTarget.evaluate(node => ({ transform: getComputedStyle(node).transform, matches: node.matches(':hover'), classes: node.className }));
|
const hovered = await hoverTarget.evaluate(node => ({ transform: getComputedStyle(node).transform, matches: node.matches(':hover'), classes: node.className }));
|
||||||
console.log('Pilot hover:', width, JSON.stringify(hovered));
|
console.log('Pilot hover:', width, JSON.stringify(hovered));
|
||||||
assert.equal(hovered.transform, 'matrix(1, 0, 0, 1, 0, -4)', 'Saved lift effect changes the rendered transform');
|
assert.equal(hovered.transform, 'matrix(1, 0, 0, 1, 0, -4)', 'Saved lift effect changes the rendered transform');
|
||||||
const target = page.locator('#kontakt');
|
const target = page.locator('#abschluss-cta');
|
||||||
assert.equal(await page.evaluate(() => motionCalls.some(call => call.id === 'kontakt')), false, 'Below-fold section must wait for scrolling');
|
assert.equal(await page.evaluate(() => motionCalls.some(call => call.id === 'abschluss-cta')), false, 'Below-fold section must wait for scrolling');
|
||||||
assert.equal(await target.evaluate(node => node.style.opacity), '0', 'Below-fold block waits out of sight, as in Flatsome');
|
assert.equal(await target.evaluate(node => node.style.opacity), '0', 'Below-fold block waits out of sight, as in Flatsome');
|
||||||
await target.scrollIntoViewIfNeeded();
|
await target.scrollIntoViewIfNeeded();
|
||||||
await page.waitForFunction(() => motionCalls.some(call => call.id === 'kontakt'));
|
await page.waitForFunction(() => motionCalls.some(call => call.id === 'abschluss-cta'));
|
||||||
const calls = await page.evaluate(() => motionCalls);
|
const calls = await page.evaluate(() => motionCalls);
|
||||||
assert.equal(calls.find(call => call.id === 'kontakt').frames[0].transform, 'translateY(40px)');
|
assert.equal(calls.find(call => call.id === 'abschluss-cta').frames[0].transform, 'translateY(40px)');
|
||||||
assert.equal(calls.find(call => call.id === 'kontakt').frames[0].opacity, 0, 'Entrance fades in from invisible, as in Flatsome');
|
assert.equal(calls.find(call => call.id === 'abschluss-cta').frames[0].opacity, 0, 'Entrance fades in from invisible, as in Flatsome');
|
||||||
await page.waitForTimeout(800);
|
await page.waitForTimeout(800);
|
||||||
await page.evaluate(() => scrollTo(0, 0)); await target.scrollIntoViewIfNeeded(); await page.waitForTimeout(100);
|
await page.evaluate(() => scrollTo(0, 0)); await target.scrollIntoViewIfNeeded(); await page.waitForTimeout(100);
|
||||||
assert.equal(await page.evaluate(() => motionCalls.filter(call => call.id === 'kontakt').length), 1, 'Entrance runs once');
|
assert.equal(await page.evaluate(() => motionCalls.filter(call => call.id === 'abschluss-cta').length), 1, 'Entrance runs once');
|
||||||
await page.screenshot({ path: `${report}/pilot-${width}.png` });
|
await page.screenshot({ path: `${report}/pilot-${width}.png` });
|
||||||
|
|
||||||
// Exercise both existing choices on the real pilot element without editing saved content.
|
// Exercise both existing choices on the real pilot element without editing saved content.
|
||||||
for (const direction of ['left', 'right']) {
|
for (const direction of ['left', 'right']) {
|
||||||
await page.evaluate(async direction => {
|
await page.evaluate(async direction => {
|
||||||
const node = document.getElementById('kontakt');
|
const node = document.getElementById('abschluss-cta');
|
||||||
node.classList.remove('fx-entrance-rise', 'fx-entrance-left', 'fx-entrance-right');
|
node.classList.remove('fx-entrance-rise', 'fx-entrance-left', 'fx-entrance-right');
|
||||||
node.classList.add(`fx-entrance-${direction}`);
|
node.classList.add(`fx-entrance-${direction}`);
|
||||||
const { initMotion } = await import(document.querySelector('script[src*="motion.js"]').src);
|
const { initMotion } = await import(document.querySelector('script[src*="motion.js"]').src);
|
||||||
window.stopMotionCheck?.(); window.motionCalls = [];
|
window.stopMotionCheck?.(); window.motionCalls = [];
|
||||||
window.stopMotionCheck = initMotion();
|
window.stopMotionCheck = initMotion();
|
||||||
}, direction);
|
}, direction);
|
||||||
await page.waitForFunction(() => motionCalls.some(call => call.id === 'kontakt'));
|
await page.waitForFunction(() => motionCalls.some(call => call.id === 'abschluss-cta'));
|
||||||
const actual = await target.evaluate(node => {
|
const actual = await target.evaluate(node => {
|
||||||
const animation = node.getAnimations()[0]; animation.pause(); animation.currentTime = 150;
|
const animation = node.getAnimations()[0]; animation.pause(); animation.currentTime = 150;
|
||||||
return { transform: getComputedStyle(node).transform, frames: animation.effect.getKeyframes() };
|
return { transform: getComputedStyle(node).transform, frames: animation.effect.getKeyframes() };
|
||||||
@@ -91,7 +91,7 @@ try {
|
|||||||
// Umut's Chrome reports "reduce" without anyone having chosen it; the site must still move.
|
// Umut's Chrome reports "reduce" without anyone having chosen it; the site must still move.
|
||||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||||
await page.reload(); await target.scrollIntoViewIfNeeded();
|
await page.reload(); await target.scrollIntoViewIfNeeded();
|
||||||
await page.waitForFunction(() => motionCalls.some(call => call.id === 'kontakt'), null, { timeout: 3000 });
|
await page.waitForFunction(() => motionCalls.some(call => call.id === 'abschluss-cta'), null, { timeout: 3000 });
|
||||||
await page.waitForTimeout(900);
|
await page.waitForTimeout(900);
|
||||||
assert.equal(await target.evaluate(node => getComputedStyle(node).opacity), '1', 'Block ends fully visible');
|
assert.equal(await target.evaluate(node => getComputedStyle(node).opacity), '1', 'Block ends fully visible');
|
||||||
await hoverTarget.scrollIntoViewIfNeeded(); await hoverTarget.hover(); await page.waitForTimeout(350);
|
await hoverTarget.scrollIntoViewIfNeeded(); await hoverTarget.hover(); await page.waitForTimeout(350);
|
||||||
@@ -116,7 +116,7 @@ try {
|
|||||||
}).observe(document, { childList: true });
|
}).observe(document, { childList: true });
|
||||||
});
|
});
|
||||||
const respectPage = await respect.newPage(); respectPage.on('pageerror', error => errors.push(error.message));
|
const respectPage = await respect.newPage(); respectPage.on('pageerror', error => errors.push(error.message));
|
||||||
const respectTarget = respectPage.locator('#kontakt'), respectHover = respectPage.locator('#leistung-app');
|
const respectTarget = respectPage.locator('#abschluss-cta'), respectHover = respectPage.locator('#leistung-app');
|
||||||
await respectPage.goto(origin); await respectTarget.scrollIntoViewIfNeeded(); await respectPage.waitForTimeout(800);
|
await respectPage.goto(origin); await respectTarget.scrollIntoViewIfNeeded(); await respectPage.waitForTimeout(800);
|
||||||
assert.equal(await respectPage.evaluate(() => motionCalls.length), 0, 'Reduced motion suppresses animations when respected');
|
assert.equal(await respectPage.evaluate(() => motionCalls.length), 0, 'Reduced motion suppresses animations when respected');
|
||||||
assert.equal(await respectTarget.evaluate(node => getComputedStyle(node).opacity), '1');
|
assert.equal(await respectTarget.evaluate(node => getComputedStyle(node).opacity), '1');
|
||||||
@@ -124,29 +124,29 @@ try {
|
|||||||
assert.equal(await respectHover.evaluate(node => getComputedStyle(node).transform), 'none', 'Reduced motion suppresses hover movement when respected');
|
assert.equal(await respectHover.evaluate(node => getComputedStyle(node).transform), 'none', 'Reduced motion suppresses hover movement when respected');
|
||||||
await respectTarget.scrollIntoViewIfNeeded();
|
await respectTarget.scrollIntoViewIfNeeded();
|
||||||
await respectPage.emulateMedia({ reducedMotion: 'no-preference' });
|
await respectPage.emulateMedia({ reducedMotion: 'no-preference' });
|
||||||
await respectPage.waitForFunction(() => motionCalls.some(call => call.id === 'kontakt'), null, { timeout: 3000 });
|
await respectPage.waitForFunction(() => motionCalls.some(call => call.id === 'abschluss-cta'), null, { timeout: 3000 });
|
||||||
await respectPage.waitForTimeout(900);
|
await respectPage.waitForTimeout(900);
|
||||||
await respectPage.emulateMedia({ reducedMotion: 'reduce' });
|
await respectPage.emulateMedia({ reducedMotion: 'reduce' });
|
||||||
await respectPage.emulateMedia({ reducedMotion: 'no-preference' });
|
await respectPage.emulateMedia({ reducedMotion: 'no-preference' });
|
||||||
await respectPage.waitForTimeout(100);
|
await respectPage.waitForTimeout(100);
|
||||||
assert.equal(await respectPage.evaluate(() => motionCalls.filter(call => call.id === 'kontakt').length), 1, 'Preference changes do not replay an already seen element');
|
assert.equal(await respectPage.evaluate(() => motionCalls.filter(call => call.id === 'abschluss-cta').length), 1, 'Preference changes do not replay an already seen element');
|
||||||
await respectHover.scrollIntoViewIfNeeded(); await respectHover.hover(); await respectPage.waitForTimeout(350);
|
await respectHover.scrollIntoViewIfNeeded(); await respectHover.hover(); await respectPage.waitForTimeout(350);
|
||||||
assert.equal(await respectHover.evaluate(node => getComputedStyle(node).transform), 'matrix(1, 0, 0, 1, 0, -4)', 'Hover resumes after allowing motion');
|
assert.equal(await respectHover.evaluate(node => getComputedStyle(node).transform), 'matrix(1, 0, 0, 1, 0, -4)', 'Hover resumes after allowing motion');
|
||||||
// Also cover a page that starts normally, then suppresses motion before reaching its content.
|
// Also cover a page that starts normally, then suppresses motion before reaching its content.
|
||||||
await respectPage.reload(); await respectPage.waitForTimeout(800);
|
await respectPage.reload(); await respectPage.waitForTimeout(800);
|
||||||
await respectPage.emulateMedia({ reducedMotion: 'reduce' });
|
await respectPage.emulateMedia({ reducedMotion: 'reduce' });
|
||||||
await respectTarget.scrollIntoViewIfNeeded(); await respectPage.waitForTimeout(100);
|
await respectTarget.scrollIntoViewIfNeeded(); await respectPage.waitForTimeout(100);
|
||||||
assert.equal(await respectPage.evaluate(() => motionCalls.filter(call => call.id === 'kontakt').length), 0);
|
assert.equal(await respectPage.evaluate(() => motionCalls.filter(call => call.id === 'abschluss-cta').length), 0);
|
||||||
assert.equal(await respectTarget.evaluate(node => getComputedStyle(node).opacity), '1', 'A waiting block is shown again when motion is reduced');
|
assert.equal(await respectTarget.evaluate(node => getComputedStyle(node).opacity), '1', 'A waiting block is shown again when motion is reduced');
|
||||||
await respectPage.emulateMedia({ reducedMotion: 'no-preference' });
|
await respectPage.emulateMedia({ reducedMotion: 'no-preference' });
|
||||||
await respectPage.waitForFunction(() => motionCalls.some(call => call.id === 'kontakt'), null, { timeout: 3000 });
|
await respectPage.waitForFunction(() => motionCalls.some(call => call.id === 'abschluss-cta'), null, { timeout: 3000 });
|
||||||
results.push({ width, inventory, calls, checks: ['scroll trigger', 'once only', 'hidden until visible', 'left/right computed movement', 'saved hover lift', 'default plays under reduce preference', 'motion-respect honours reduce', 'resume without reloading after initial and subsequent reduced motion', 'hover resumes after preference change'] });
|
results.push({ width, inventory, calls, checks: ['scroll trigger', 'once only', 'hidden until visible', 'left/right computed movement', 'saved hover lift', 'default plays under reduce preference', 'motion-respect honours reduce', 'resume without reloading after initial and subsequent reduced motion', 'hover resumes after preference change'] });
|
||||||
await respect.close();
|
await respect.close();
|
||||||
}
|
}
|
||||||
const noJs = await browser.newContext({ javaScriptEnabled: false });
|
const noJs = await browser.newContext({ javaScriptEnabled: false });
|
||||||
const page = await noJs.newPage(); await page.goto(origin);
|
const page = await noJs.newPage(); await page.goto(origin);
|
||||||
assert.ok(await page.locator('main').innerText());
|
assert.ok(await page.locator('main').innerText());
|
||||||
assert.equal(await page.locator('#kontakt').evaluate(node => getComputedStyle(node).opacity), '1');
|
assert.equal(await page.locator('#abschluss-cta').evaluate(node => getComputedStyle(node).opacity), '1');
|
||||||
await noJs.close();
|
await noJs.close();
|
||||||
const editorContext = await browser.newContext({ reducedMotion: 'reduce', viewport: { width: 1600, height: 1000 } });
|
const editorContext = await browser.newContext({ reducedMotion: 'reduce', viewport: { width: 1600, height: 1000 } });
|
||||||
const editor = await editorContext.newPage(); editor.on('pageerror', error => errors.push(error.message));
|
const editor = await editorContext.newPage(); editor.on('pageerror', error => errors.push(error.message));
|
||||||
|
|||||||
Reference in new issue
Block a user