Beitragskarten reagieren auf Zeiger und Tastatur
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s

Umut: "es gibt keinen Mausover-Effekt, das fehlt". Am Original
nachgemessen und uebernommen: die Karte hebt sich um 8px mit weichem
Schatten, das Bild zieht auf 1,06 auf, darueber blendet ein Hinweis ein.

Die Bewegung steht vollstaendig hinter der Bewegungsschranke - wer
weniger Bewegung eingestellt hat, bekommt keine, sieht den Hinweis aber
trotzdem. Alles haengt zusaetzlich an :focus-within, damit der Effekt
nicht nur Mausbenutzern gehoert. Der Hinweis ist fuer Vorlesesoftware
ausgeblendet, weil der Titel darunter zum selben Ziel fuehrt.

Ausserdem: der Leuchteffekt trug noch das Gruen der Vorlage statt der
Akzentfarbe der Website.

Geprueft: 391 Tests unter PHP 8.2 und 8.4, Editortest, Browserpruefung,
axe ohne Verstoss.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5 committed 2026-09-13 09:09:10 +02:00
1 parent 5f23194760
commit 1b164f4c86
5 files changed
+83 -2

No files matched your search

+18
View File
@@ -440,6 +440,24 @@ Vom Verweis bleibt nur der Host. Die vollständige Adresse, aus der jemand kam,
**Aufbewahrung:** nach 60 Tagen werden die Stunden eines Tages zu einer Zeile verschmolzen, nach 425 Tagen wird gelöscht. Gut ein Jahr, damit ein Vergleich mit dem Vorjahresmonat möglich bleibt. `cms:stats:prune` macht beides und ist für einen Cronjob gedacht.
## ADR 045 – Der Zeigereffekt, und warum Feinschliff kein Zusatz ist (13.09.2026)
Umut: „es gibt keinen Mausover-Effekt wenn man auf einen Beitrag geht, das fehlt … grob siehts ähnlich aus, aber im Detail fehlen viele Kleinigkeiten.“ Das ist die richtige Beobachtung: eine Karte, die sich beim Zeigen nicht rührt, wirkt kaputt, und zwar bevor jemand sagen kann, warum.
**Die Beitragskarte reagiert jetzt**, und zwar wie im Original nachgemessen: die Karte hebt sich um 8 px, bekommt einen weichen Schatten, das Bild zieht auf 1,06 auf, und über dem Bild blendet ein Hinweis ein. Dauer und Kurven sind aus dem Original übernommen (0,28 s für die Karte, 0,5 s für das Bild, 0,3 s für den Hinweis).
**Drei Entscheidungen dabei:**
- **Die Bewegung steht vollständig hinter der Bewegungsschranke.** Wer im Betriebssystem weniger Bewegung eingestellt hat, bekommt keine sanftere Fahrt, sondern keine – aber den Hinweis über dem Bild trotzdem, denn der ist Information und keine Verzierung. Ein Test prüft genau diese Trennung: kein `translateY` außerhalb der Schranke.
- **Auch mit der Tastatur.** Alles hängt zusätzlich an `:focus-within`, sonst wäre der Effekt eine Belohnung für Mausbenutzer.
- **Der Hinweis ist für Vorlesesoftware ausgeblendet.** Er wiederholt nur, wohin der Titel darunter ohnehin führt; zweimal dasselbe Ziel anzusagen hilft niemandem.
**Der Hero war das falsche Bild.** Im Original ist er kein Foto, sondern Marineblau mit einer Binärtextur bei 10 Prozent und einem roten Schein oben rechts. Nachgebaut mit den Mitteln, die der Designer hat: dasselbe Bild als Textur, die Überlagerung auf 93 Prozent, und ein Verlauf schräg darüber für den Schein. Die Augenbraue ist im Original eine **Pille** mit Rahmen, kein nackter Text – auch das ließ sich ohne neue Funktion bauen.
**Und die Effekte, die es längst gibt, waren schlicht nicht benutzt.** `layout.effects` kennt Anheben, Zoom, Schatten und Einblenden seit Dokumentschema v3; im Nachbau standen sie auf „nichts“. Jetzt heben sich Leistungskarten und Lösungskarten beim Zeigen. Das ist keine Änderung am CMS, sondern die Antwort auf die Frage, warum es „im Detail“ fehlte: ein Werkzeug, das man nicht in die Hand nimmt, ist so gut wie nicht vorhanden.
**Nebenbei entfernt:** der Leuchteffekt (`fx-hover-glow`) trug noch das Grün der Vorlage statt der Akzentfarbe der Website.
## ADR 044 – Genauer hinsehen: Punkte statt Rollleiste, Karten statt Zeilen (13.09.2026)
Umut hat den Nachbau Detail für Detail mit dem Original verglichen, und er hatte in jedem Punkt recht. Was dabei herauskam, sind nicht Geschmacksfragen, sondern vier Stellen, an denen das CMS etwas nicht konnte oder etwas Falsches tat.
+23 -1
View File
@@ -24,7 +24,7 @@
.fx-hover-sink { transition: transform .2s ease }
.fx-hover-sink:hover { transform: translateY(3px) }
.fx-hover-glow { transition: box-shadow .2s ease }
.fx-hover-glow:hover { box-shadow: 0 0 0 4px #2c6b4f26, 0 10px 30px #1b2a2426 }
.fx-hover-glow:hover { box-shadow: 0 0 0 4px color-mix(in srgb,var(--accent) 18%,transparent), 0 10px 30px color-mix(in srgb,var(--ink) 16%,transparent) }
.fx-hover-tilt { transition: transform .2s ease }
.fx-hover-tilt:hover { transform: rotate(-1.2deg) scale(1.01) }
.fx-hover-grow-border { transition: box-shadow .2s ease }
@@ -36,3 +36,25 @@
@keyframes kant-zoom { from { opacity: 0; transform: scale(.92) } to { opacity: 1; transform: none } }
@keyframes kant-flip { from { opacity: 0; transform: perspective(800px) rotateX(-12deg) } to { opacity: 1; transform: none } }
@keyframes kant-unblur { from { opacity: 0; filter: blur(8px) } to { opacity: 1; filter: none } }
/* Beitragskarten und Knöpfe, ergänzt am 13.09.2026.
Umut hat gefragt, wo der Zeigereffekt bleibt: eine Karte, die sich beim Zeigen nicht rührt,
wirkt tot. Angehoben wird die ganze Karte, das Bild zieht leicht auf, und der Hinweis darüber
blendet ein. Alles innerhalb der Bewegungsschranke - wer weniger Bewegung eingestellt hat,
bekommt den Hinweis trotzdem, nur ohne Fahrt. */
@media (prefers-reduced-motion: no-preference) {
.post-card { transition: transform .28s ease, box-shadow .28s ease, border-color .28s ease }
.post-card:hover, .post-card:focus-within {
transform: translateY(-8px);
box-shadow: 0 18px 50px -16px color-mix(in srgb, var(--ink) 28%, transparent);
border-color: transparent;
}
.post-image img { transition: transform .5s ease }
.post-card:hover .post-image img, .post-card:focus-within .post-image img { transform: scale(1.06) }
.post-view { transition: opacity .3s ease }
/* Der Knopf hebt sich beim Zeigen um zwei Pixel - dieselbe Geste wie auf der Karte. */
.button { transition: transform .2s ease, background-color .2s ease, border-color .2s ease }
.button:hover { transform: translateY(-2px) }
}
+1 -1
View File
@@ -23,7 +23,7 @@ p{hyphens:auto}
/* Posts */
.post-list{list-style:none;margin:0;padding:0;display:grid;gap:26px;grid-template-columns:repeat(auto-fill,minmax(260px,1fr))}
.post-card{display:flex;flex-direction:column;background:var(--paper);border:1px solid var(--line);border-radius:18px;overflow:hidden}.post-card>.post-body{display:grid;gap:10px;padding:20px 22px 22px}
.post-image{display:block}.post-image img{width:100%;height:auto;aspect-ratio:16/10;object-fit:cover;border-radius:0;display:block;background:var(--ink)}
.post-image{display:block;position:relative;overflow:hidden}.post-view{position:absolute;inset:0;display:grid;place-items:center;background:color-mix(in srgb,var(--ink) 55%,transparent);color:#fff;font-weight:700;font-size:15px;letter-spacing:.4px;opacity:0}.post-card:hover .post-view,.post-card:focus-within .post-view{opacity:1}.post-image img{width:100%;height:auto;aspect-ratio:16/10;object-fit:cover;border-radius:0;display:block;background:var(--ink)}
.kant h1 mark,.kant h2 mark,.kant h3 mark{background:transparent;color:var(--accent);padding:0}
.kant .post-card h3{margin:0;font-size:19px;font-weight:800;line-height:1.12;color:var(--ink);letter-spacing:-.2px}
.post-card h3 a{text-decoration:none}
+3
View File
@@ -9,6 +9,9 @@
<a class="post-image" href="{{ post.href }}" tabindex="-1" aria-hidden="true">
<img src="{{ post.image.variants|last.src }}" alt="" width="{{ post.image.variants|last.width }}" height="{{ post.image.variants|last.height }}" loading="lazy" decoding="async"
srcset="{% for source in post.image.variants %}{{ source.src }} {{ source.width }}w{% if not loop.last %}, {% endif %}{% endfor %}" sizes="(max-width: 650px) 100vw, 380px">
{# Der Hinweis liegt über dem Bild und erscheint beim Zeigen. Für Vorlesesoftware ist
er nichts Neues - der Titel darunter ist derselbe Verweis -, deshalb ausgeblendet. #}
<span class="post-view" aria-hidden="true">Ansehen</span>
</a>
{% endif %}
<div class="post-body">
+38
View File
@@ -678,6 +678,44 @@ final class CmsTest extends TestCase
}
}
public function testAPostCardReactsToThePointerAndToTheKeyboard(): void
{
// Umut: „es gibt keinen Mausover-Effekt, das fehlt“. Eine Karte, die sich beim Zeigen
// nicht rührt, wirkt tot. Der Hinweis liegt über dem Bild, ist für Vorlesesoftware aber
// nichts Neues - der Titel darunter führt zum selben Ziel.
$this->publishedPost('projekt-eins', 'Projekt eins', '2026-02-01', ['Projekte'], []);
// Der Hinweis liegt über dem Beitragsbild, also braucht der Beitrag eines.
$media = new MediaLibrary($this->db, $this->path.'-media');
$file = tempnam(sys_get_temp_dir(), 'kant-karte-');
$image = imagecreatetruecolor(600, 400);
imagepng($image, $file);
imagedestroy($image);
$item = $media->upload(new UploadedFile($file, 'karte.png', test: true));
@unlink($file);
$id = $this->db->fetchOne('SELECT id FROM page WHERE path = ?', ['projekt-eins']);
$this->posts->saveMetadata($id, ['featuredMediaId' => $item['id'], 'publishedAt' => '2026-02-01'], ['Projekte'], [], (int) $this->db->fetchOne('SELECT version FROM page WHERE id = ?', [$id]));
$document = DemoContent::starter('Startseite');
$document['schemaVersion'] = 17;
$document['blocks'][] = ['id' => 'raster', 'type' => 'postlist', 'props' => ['count' => 5, 'category' => 'projekte', 'tag' => '']];
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
$html = $this->request('/')->getContent();
self::assertStringContainsString('<span class="post-view" aria-hidden="true">Ansehen</span>', $html);
// Die Bewegung steht vollständig hinter der Bewegungsschranke, der Hinweis selbst nicht:
// wer weniger Bewegung eingestellt hat, sieht ihn trotzdem.
$effects = (string) file_get_contents(__DIR__.'/../public/assets/enhancements.css');
$guarded = explode('@media (prefers-reduced-motion: no-preference) {', $effects);
self::assertStringContainsString('.post-card:hover, .post-card:focus-within {', end($guarded));
self::assertStringContainsString('transform: scale(1.06)', end($guarded));
self::assertStringNotContainsString('transform: translateY(-8px)', $guarded[0], 'Keine Bewegung außerhalb der Schranke.');
// Und über die Tastatur erreichbar: der Zustand hängt auch an :focus-within.
$site = (string) file_get_contents(__DIR__.'/../public/assets/site.css');
self::assertStringContainsString('.post-card:hover .post-view,.post-card:focus-within .post-view{opacity:1}', $site);
}
public function testAPostListCanLookLikeAProjectGridInsteadOfABlogArchive(): void
{
// Beim Nachbau von kant-it.de fehlte genau das: das Projektraster dort zeigt Bild und