Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
769b0fae2d | ||
|
|
9c8462d46d |
No files matched your search
@@ -4,6 +4,10 @@ Lokaler CMS-Prototyp für PHP-Webhosting mit erstem visuellen Editor. Symfony 7.
|
|||||||
|
|
||||||
Die übernommenen Originaldokumente sind `AGENTS.md` und `ENTWICKLUNGSPLAN.md`. Den tatsächlichen Stand enthält `docs/STATUS.md`, Entscheidungen und Datenmodell stehen in `docs/ARCHITECTURE.md`.
|
Die übernommenen Originaldokumente sind `AGENTS.md` und `ENTWICKLUNGSPLAN.md`. Den tatsächlichen Stand enthält `docs/STATUS.md`, Entscheidungen und Datenmodell stehen in `docs/ARCHITECTURE.md`.
|
||||||
|
|
||||||
|
## Slider auf dem iPhone (ab 1.11.2)
|
||||||
|
|
||||||
|
Safari bricht sanftes Weiterblättern in einem einrastenden Slider ab und springt zur vorigen Folie zurück; die Automatik und die Pfeile stotterten deshalb auf dem iPhone. Während der Fahrt ist das Einrasten jetzt ausgeschaltet und wird bei Ankunft auf der Folie wieder eingeschaltet. Die Punkte unter dem Slider sind echte Punkte, der aktive gefüllt, bei unveränderter Trefffläche von 44 px.
|
||||||
|
|
||||||
## Kopfbereich beim Scrollen und gleich hohe Karten (ab 1.11.1)
|
## Kopfbereich beim Scrollen und gleich hohe Karten (ab 1.11.1)
|
||||||
|
|
||||||
Der Kopfbereich kann für die gescrollte Seite eigene Farben tragen, wie der transparente Header in Flatsome: Kopfbereich im Editor öffnen, die Sektion wählen, **Design → Hintergrund**. Dort steht unter „Hintergrundfarbe“ die Zeile **„Hintergrundfarbe beim Scrollen“**; dasselbe gibt es für Textfarbe, Rahmenfarbe, Schattenfarbe und Deckkraft. Der normale Wert gilt am Seitenanfang, der Scroll-Wert sobald die Seite mehr als 40 px bewegt wurde, mit weichem Übergang. Für einen durchsichtigen Kopf am Anfang `#ffffff00` als normale Hintergrundfarbe eintragen. Damit der Kopf über der ersten Sektion liegt statt darüber zu stehen, unter **Einstellungen** „Kopfbereich über dem Inhalt schweben lassen“ einschalten; zusammen mit „oben halten“ bleibt er beim Scrollen sichtbar. Ohne JavaScript behält der Kopf seine normalen Farben.
|
Der Kopfbereich kann für die gescrollte Seite eigene Farben tragen, wie der transparente Header in Flatsome: Kopfbereich im Editor öffnen, die Sektion wählen, **Design → Hintergrund**. Dort steht unter „Hintergrundfarbe“ die Zeile **„Hintergrundfarbe beim Scrollen“**; dasselbe gibt es für Textfarbe, Rahmenfarbe, Schattenfarbe und Deckkraft. Der normale Wert gilt am Seitenanfang, der Scroll-Wert sobald die Seite mehr als 40 px bewegt wurde, mit weichem Übergang. Für einen durchsichtigen Kopf am Anfang `#ffffff00` als normale Hintergrundfarbe eintragen. Damit der Kopf über der ersten Sektion liegt statt darüber zu stehen, unter **Einstellungen** „Kopfbereich über dem Inhalt schweben lassen“ einschalten; zusammen mit „oben halten“ bleibt er beim Scrollen sichtbar. Ohne JavaScript behält der Kopf seine normalen Farben.
|
||||||
|
|||||||
+11
-1
@@ -1,12 +1,22 @@
|
|||||||
# Projektstatus
|
# Projektstatus
|
||||||
|
|
||||||
|
## 1.11.2 – Slider stottert nicht mehr auf dem iPhone (14.09.2026)
|
||||||
|
|
||||||
|
**Anlass:** Umuts iPhone-Screenshot des Hero-Sliders („das buggt“). Safari bricht `scrollTo({behavior:'smooth'})` in einem Container mit `scroll-snap-type: x mandatory` ab und rastet auf die vorige Folie zurück; Pfeile und Automatik ruckeln.
|
||||||
|
|
||||||
|
**Umgesetzt:** `slider.js` schaltet das Einrasten für die Fahrt aus und stellt es bei Ankunft am Zielwert wieder her (spätestens nach zwei Sekunden, weil ein Hintergrund-Tab sanftes Scrollen pausiert); bei reduzierter Bewegung weiter sofortiges Springen. Die Punkte der Steuerung sind gezeichnete Punkte (aktiv gefüllt) statt eines Kastens um den Text, Trefffläche bleibt 44 px.
|
||||||
|
|
||||||
|
**Geprüft:** Playwright auf der Pilotkopie bei 375 und 1600 px: „Nächste Folie“ landet exakt auf dem Folienanfang (359 bzw. 666 px), Einrasten danach wieder `x mandatory`, Automatik blättert nach 7 s selbst weiter; 33 JavaScript-Tests. Der Safari-Fall selbst ist hier nicht ausführbar; die Maßnahme ist die dokumentierte Umgehung dieses WebKit-Verhaltens.
|
||||||
|
|
||||||
## 1.11.1 – Kopfbereich mit Scroll-Farben, gleich hohe Karten (14.09.2026)
|
## 1.11.1 – Kopfbereich mit Scroll-Farben, gleich hohe Karten (14.09.2026)
|
||||||
|
|
||||||
**Anlass:** Umut sah auf der Startseite ungleich hohe Karten und wünschte sich für den Kopfbereich einstellbare Transparenz-Effekte beim Scrollen, wie in Flatsome.
|
**Anlass:** Umut sah auf der Startseite ungleich hohe Karten und wünschte sich für den Kopfbereich einstellbare Transparenz-Effekte beim Scrollen, wie in Flatsome.
|
||||||
|
|
||||||
**Umgesetzt:** (1) `.block-iconcard + .block-iconcard` und die verwandten Stapelabstände gelten in `.block-row` nicht mehr; die Karten in einer Zeile sind gleich hoch. (2) Neuer Zustand `scrolled` neben `hover` in der Gestaltung: `StyleSchema::scrollable()` (Hintergrund-, Text-, Rahmen-, Schattenfarbe, Deckkraft), Prüfung im `StyleCompiler`, Ausgabe als `html.is-scrolled #id{…}` mit Übergang; Design-Panel zeigt „… beim Scrollen“ nur im Kopfbereich; `scrolled.js` markiert die Seite ab 40 px, wird nur geladen, wenn der Kopfbereich Scroll-Werte hat (Seiten und Archive, CSP entsprechend); die Editor-Vorschau setzt die Klasse selbst. Neue Einstellung „Kopfbereich über dem Inhalt schweben lassen“ (`headerOverlay`, Klasse `is-overlay`, absolut bzw. fixiert mit „oben halten“). ADR 092.
|
**Umgesetzt:** (1) `.block-iconcard + .block-iconcard` und die verwandten Stapelabstände gelten in `.block-row` nicht mehr; die Karten in einer Zeile sind gleich hoch. (2) Neuer Zustand `scrolled` neben `hover` in der Gestaltung: `StyleSchema::scrollable()` (Hintergrund-, Text-, Rahmen-, Schattenfarbe, Deckkraft), Prüfung im `StyleCompiler`, Ausgabe als `html.is-scrolled #id{…}` mit Übergang; Design-Panel zeigt „… beim Scrollen“ nur im Kopfbereich; `scrolled.js` markiert die Seite ab 40 px, wird nur geladen, wenn der Kopfbereich Scroll-Werte hat (Seiten und Archive, CSP entsprechend); die Editor-Vorschau setzt die Klasse selbst. Neue Einstellung „Kopfbereich über dem Inhalt schweben lassen“ (`headerOverlay`, Klasse `is-overlay`, absolut bzw. fixiert mit „oben halten“). ADR 092.
|
||||||
|
|
||||||
**Geprüft:** Neuer Test `testAHeaderCanChangeItsColoursOnceThePageHasScrolled` (Ausgabe, Skript, CSP, Ablehnung unzulässiger Scroll-Werte); PHP-Suite 485 Tests grün bis auf den bekannten Altfehler. In der App-Vorschau: sechs Leistungskarten 227 px, Kundenstimmen 232 px, „Warum“-Karten 191 px, gestapelte Karten behalten 18 px. Scroll-Farbe im Kopfbereich-Editor über das Farbfeld gesetzt, in der Vorschau als Regel angekommen und gespeichert. 33 JavaScript-Tests, Twig-Lint. Editor-Browserprüfung siehe Veröffentlichung.
|
**Geprüft:** Neuer Test `testAHeaderCanChangeItsColoursOnceThePageHasScrolled` (Ausgabe, Skript, CSP, Ablehnung unzulässiger Scroll-Werte); PHP-Suite 485 Tests grün bis auf den bekannten Altfehler. In der App-Vorschau: sechs Leistungskarten 227 px, Kundenstimmen 232 px, „Warum“-Karten 191 px, gestapelte Karten behalten 18 px. Scroll-Farbe im Kopfbereich-Editor über das Farbfeld gesetzt, in der Vorschau als Regel angekommen und gespeichert. 33 JavaScript-Tests, Twig-Lint aller 49 Vorlagen, PHP-Stilprüfung. Editor-Browserprüfung vollständig erfolgreich, einschließlich aller axe-Prüfungen.
|
||||||
|
|
||||||
|
**Veröffentlicht:** Commit `3785d3d` und Tag `v1.11.1` auf `origin`; [Release 1.11.1](https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.11.1) mit ZIP (3547 Dateien, 4,7 MB), SHA-256-Datei und `install.php` als neuestes stabiles Release angelegt. Anhänge anonym heruntergeladen und bytegenau mit dem lokalen Build verglichen; SHA-256 `970a12bc…6ff306`.
|
||||||
|
|
||||||
## Kant-IT-Startseite: Einblendungen nach Flatsome-Muster in `var/kantit.db` (14.09.2026)
|
## Kant-IT-Startseite: Einblendungen nach Flatsome-Muster in `var/kantit.db` (14.09.2026)
|
||||||
|
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ body.consent-open{padding-bottom:260px}
|
|||||||
.slider-dots{position:relative}
|
.slider-dots{position:relative}
|
||||||
@supports selector(::scroll-marker){.slider-dots:not(.slider-controlled){scroll-marker-group:after;scroll-behavior:smooth;padding-bottom:0;scrollbar-width:none;scrollbar-gutter:auto}.slider-dots:not(.slider-controlled)::-webkit-scrollbar{display:none}.slider-dots:not(.slider-controlled)>*::scroll-marker{content:'';width:12px;height:12px;border-radius:50%;background:color-mix(in srgb,currentColor 30%,transparent);margin:0 6px;cursor:pointer}.slider-dots>*::scroll-marker:target-current{background:var(--accent)}.slider-dots::scroll-marker-group{display:flex;justify-content:center;align-items:center;padding:14px 0 2px}}
|
@supports selector(::scroll-marker){.slider-dots:not(.slider-controlled){scroll-marker-group:after;scroll-behavior:smooth;padding-bottom:0;scrollbar-width:none;scrollbar-gutter:auto}.slider-dots:not(.slider-controlled)::-webkit-scrollbar{display:none}.slider-dots:not(.slider-controlled)>*::scroll-marker{content:'';width:12px;height:12px;border-radius:50%;background:color-mix(in srgb,currentColor 30%,transparent);margin:0 6px;cursor:pointer}.slider-dots>*::scroll-marker:target-current{background:var(--accent)}.slider-dots::scroll-marker-group{display:flex;justify-content:center;align-items:center;padding:14px 0 2px}}
|
||||||
.slider-controlled{scrollbar-width:none;scrollbar-gutter:auto}.slider-controlled::-webkit-scrollbar{display:none}
|
.slider-controlled{scrollbar-width:none;scrollbar-gutter:auto}.slider-controlled::-webkit-scrollbar{display:none}
|
||||||
.slider-controls{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:8px;min-height:48px;margin-top:8px;padding:4px;font:14px/1.4 system-ui,sans-serif}.slider-controls button{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:40px;padding:8px 12px;border:1px solid #b2c2b8;border-radius:7px;background:#fff;color:#193c2d;cursor:pointer;font:inherit}.slider-controls button:focus-visible{outline:3px solid #b55b12;outline-offset:3px}.slider-controls button:hover{background:#e4efe7}.slider-controls button:disabled{opacity:.6;cursor:default}.slider-position{font-variant-numeric:tabular-nums;min-width:44px;text-align:center}.slider-control-dots{display:flex;flex-wrap:wrap;gap:3px}.slider-control-dots button{min-width:30px;padding:5px;border:0;background:transparent;color:inherit;opacity:.5}.slider-control-dots button[aria-pressed=true]{opacity:1;outline:1px solid currentColor;outline-offset:-5px}.slider-controls[hidden]{display:none}
|
.slider-controls{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:8px;min-height:48px;margin-top:8px;padding:4px;font:14px/1.4 system-ui,sans-serif}.slider-controls button{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:40px;padding:8px 12px;border:1px solid #b2c2b8;border-radius:7px;background:#fff;color:#193c2d;cursor:pointer;font:inherit}.slider-controls button:focus-visible{outline:3px solid #b55b12;outline-offset:3px}.slider-controls button:hover{background:#e4efe7}.slider-controls button:disabled{opacity:.6;cursor:default}.slider-position{font-variant-numeric:tabular-nums;min-width:44px;text-align:center}.slider-control-dots{display:flex;flex-wrap:wrap;gap:3px}.slider-control-dots button{min-width:32px;padding:0;border:0;background:transparent;color:inherit;font-size:0}.slider-control-dots button::before{content:'';width:10px;height:10px;border-radius:50%;border:2px solid currentColor;background:transparent;transition:background-color .2s}.slider-control-dots button[aria-pressed=true]::before{background:currentColor}.slider-control-dots button:hover::before{background:color-mix(in srgb,currentColor 50%,transparent)}.slider-controls[hidden]{display:none}
|
||||||
@media(max-width:650px){.slider-controls button{min-height:44px;min-width:44px}}
|
@media(max-width:650px){.slider-controls button{min-height:44px;min-width:44px}}
|
||||||
|
|
||||||
.slide-desktop-1{grid-auto-columns:100%}.slide-desktop-2{grid-auto-columns:calc(50% - 10px)}
|
.slide-desktop-1{grid-auto-columns:100%}.slide-desktop-2{grid-auto-columns:calc(50% - 10px)}
|
||||||
|
|||||||
+18
-2
@@ -28,9 +28,25 @@ export function initSliders(doc = document) {
|
|||||||
available().forEach(i => { const delta = Math.abs(left(slides[i]) - slider.scrollLeft); if (delta < distance) { distance = delta; index = i; } });
|
available().forEach(i => { const delta = Math.abs(left(slides[i]) - slider.scrollLeft); if (delta < distance) { distance = delta; index = i; } });
|
||||||
return index;
|
return index;
|
||||||
};
|
};
|
||||||
|
let settle = null;
|
||||||
const go = index => {
|
const go = index => {
|
||||||
if (!available().includes(index)) return;
|
if (!available().includes(index)) return;
|
||||||
slider.scrollTo({ left: Math.max(0, Math.min(slider.scrollWidth - slider.clientWidth, left(slides[index]))), behavior: reduced() ? 'instant' : 'smooth' });
|
const target = Math.max(0, Math.min(slider.scrollWidth - slider.clientWidth, left(slides[index])));
|
||||||
|
if (reduced()) { slider.scrollTo({ left: target, behavior: 'instant' }); return; }
|
||||||
|
// Safari cancels a smooth scroll on a snapping container and snaps back to the slide it
|
||||||
|
// came from - the slider stutters instead of moving on. Snapping is switched off for the
|
||||||
|
// ride and put back once the movement has settled, on the exact start of a slide, so
|
||||||
|
// restoring it moves nothing.
|
||||||
|
clearInterval(settle);
|
||||||
|
const snap = slider.style.scrollSnapType;
|
||||||
|
slider.style.scrollSnapType = 'none';
|
||||||
|
slider.scrollTo({ left: target, behavior: 'smooth' });
|
||||||
|
// Restored on arrival, or after two seconds if the ride was interrupted - a background tab
|
||||||
|
// pauses smooth scrolling, and snapping must not come back halfway.
|
||||||
|
let ticks = 0;
|
||||||
|
settle = setInterval(() => {
|
||||||
|
if (Math.abs(slider.scrollLeft - target) < 1 || ++ticks > 25) { clearInterval(settle); settle = null; slider.style.scrollSnapType = snap; }
|
||||||
|
}, 80);
|
||||||
};
|
};
|
||||||
const step = direction => {
|
const step = direction => {
|
||||||
const visible = available(), position = visible.indexOf(active());
|
const visible = available(), position = visible.indexOf(active());
|
||||||
@@ -82,7 +98,7 @@ export function initSliders(doc = document) {
|
|||||||
const resize = new view.ResizeObserver(update); resize.observe(slider);
|
const resize = new view.ResizeObserver(update); resize.observe(slider);
|
||||||
preference.addEventListener('change', () => { if (reduced()) pause(); update(); }, { signal });
|
preference.addEventListener('change', () => { if (reduced()) pause(); update(); }, { signal });
|
||||||
schedule();
|
schedule();
|
||||||
cleanups.push(() => { clearTimeout(timer); events.abort(); observer.disconnect(); resize.disconnect(); controls.remove(); slider.classList.remove('slider-controlled'); delete slider.dataset.enhanced; });
|
cleanups.push(() => { clearTimeout(timer); clearInterval(settle); slider.style.scrollSnapType = ''; events.abort(); observer.disconnect(); resize.disconnect(); controls.remove(); slider.classList.remove('slider-controlled'); delete slider.dataset.enhanced; });
|
||||||
});
|
});
|
||||||
return () => cleanups.forEach(cleanup => cleanup());
|
return () => cleanups.forEach(cleanup => cleanup());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user