From 51e5d6757663da13ea19177788d7a1b708cdcc36 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Wed, 4 Nov 2026 13:49:29 +0100 Subject: [PATCH] feat!: one link per post card and the heading convention - JournalPostCard: the title link is the card's only anchor; the image is no longer a link and the separate read-more link is gone; the whole card stays clickable through a CSS stretched link with the focus ring on the card; prop headingLevel (2 | 3, default 2) - related posts keep their h2 and render their cards at h3; JournalPostList passes headingLevel through - smoke checks headings=strict (one h1, no skipped level) and cardlinks=N on every fixture page; expectation files for postTitleTag forced to h1 and p (the documented two-h1 and no-h1 consequences) BREAKING CHANGE: .journal-post-card__more and the image link (.journal-post-card__image is now a div) are removed from post cards. --- app/assets/css/journal-base.css | 19 +++++++--- app/components/Journal/PostCard.vue | 19 ++++++---- app/components/Journal/PostList.vue | 5 +-- app/components/Journal/RelatedPosts.vue | 5 +-- package.json | 2 +- testing/expect-acme-title-h1.txt | 7 ++++ testing/expect-acme-title-p.txt | 7 ++++ testing/expect-acme.txt | 16 +++++++++ testing/smoke-ssr.mjs | 47 +++++++++++++++++++++++-- 9 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 testing/expect-acme-title-h1.txt create mode 100644 testing/expect-acme-title-p.txt diff --git a/app/assets/css/journal-base.css b/app/assets/css/journal-base.css index 8561a2b..7675430 100644 --- a/app/assets/css/journal-base.css +++ b/app/assets/css/journal-base.css @@ -203,7 +203,10 @@ color: var(--journal-color-muted); } +/* The card is one link (UI-SPEC L5): the title anchor stretches over the + whole card, and the card shows the keyboard focus. */ .journal-post-card { + position: relative; display: flex; flex-direction: column; gap: var(--journal-space-2); @@ -213,6 +216,11 @@ background: var(--journal-color-bg); } +.journal-post-card:has(.journal-post-card__title a:focus-visible) { + outline: 2px solid var(--journal-color-accent); + outline-offset: 2px; +} + .journal-post-card .journal-featured-image { margin: calc(-1 * var(--journal-space-4)) calc(-1 * var(--journal-space-4)) var(--journal-space-2); } @@ -230,6 +238,13 @@ text-decoration: none; } +.journal-post-card__title a::after { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; +} + .journal-post-card__date { color: var(--journal-color-muted); font-size: 0.85em; @@ -239,10 +254,6 @@ margin: 0; } -.journal-post-card__more { - margin-top: auto; -} - .journal-related { margin-top: var(--journal-space-5); } diff --git a/app/components/Journal/PostCard.vue b/app/components/Journal/PostCard.vue index 1c4e396..ae7ca1c 100644 --- a/app/components/Journal/PostCard.vue +++ b/app/components/Journal/PostCard.vue @@ -1,26 +1,31 @@ diff --git a/app/components/Journal/PostList.vue b/app/components/Journal/PostList.vue index 59eeba3..4f85a35 100644 --- a/app/components/Journal/PostList.vue +++ b/app/components/Journal/PostList.vue @@ -1,13 +1,14 @@ diff --git a/app/components/Journal/RelatedPosts.vue b/app/components/Journal/RelatedPosts.vue index 1b4c59e..23b7504 100644 --- a/app/components/Journal/RelatedPosts.vue +++ b/app/components/Journal/RelatedPosts.vue @@ -1,6 +1,7 @@