feat(gallery): grid title, subtle download icon, full i18n
Deploy / verify (push) Successful in 38s
Deploy / deploy (push) Successful in 59s

- Render block.title as <h3> above the grid when set; the carousel
  variant is unchanged.
- Download CTA in the modal shrunk to a 9×9 icon-only button with
  text-white/80 hover-tint instead of the dark green link colour the
  app's `main a` rule was forcing in. Adds `no-underline` so the
  global underline doesn't sneak back in.
- All previously hardcoded German strings (Download, Schließen,
  modal aria, open aria) now go through translations.ts → CMS
  translation_bundle. Cms_content app.json5 ships the matching keys.

Open follow-up: per-image title/alt/caption is empty on the existing
windwiderstand mediathek gallery → editor needs to fill those if
hover labels are wanted.
This commit is contained in:
Peter Meier
2026-04-25 10:44:30 +02:00
parent 9946061105
commit 998fba8904
2 changed files with 14 additions and 7 deletions
@@ -167,6 +167,9 @@
{#if withUrl.length === 0}
<p class="text-sm text-stein-500">{t(T.image_gallery_empty)}</p>
{:else if block.variant === "grid"}
{#if block.title}
<h3 class="text-lg font-semibold text-stein-900 mb-3">{block.title}</h3>
{/if}
<ul class="not-prose grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4">
{#each withUrl as item, i}
{@const label = imageLabel(item.img)}
@@ -175,7 +178,7 @@
type="button"
class="group relative block w-full aspect-square overflow-hidden rounded-sm bg-stein-100 cursor-zoom-in focus:outline-none focus:ring-2 focus:ring-wald-500"
onclick={() => openModal(i)}
aria-label={label || `Bild ${i + 1}`}
aria-label={label || t(T.image_gallery_open_aria)}
>
<RustyImage
src={item.url}
@@ -207,7 +210,7 @@
class="fixed inset-0 z-[100] flex items-center justify-center bg-black/85 p-4"
role="dialog"
aria-modal="true"
aria-label={curLabel || "Bild-Vorschau"}
aria-label={curLabel || t(T.image_gallery_modal_aria)}
onclick={closeModal}
transition:fade={{ duration: 150 }}
>
@@ -216,7 +219,6 @@
onclick={(e) => e.stopPropagation()}
role="presentation"
>
<!-- eslint-disable-next-line @typescript-eslint/no-unused-expressions -->
<img
src={cur.url}
alt={curLabel}
@@ -235,10 +237,11 @@
<a
href={cur.url}
download={downloadFilename(cur.url)}
class="inline-flex items-center gap-1.5 rounded-md bg-white/10 px-3 py-1.5 text-sm font-medium text-white hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white/50"
class="no-underline inline-flex h-9 w-9 items-center justify-center rounded-full text-white/80 hover:text-white hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-white/40"
title={t(T.image_gallery_download)}
aria-label={t(T.image_gallery_download)}
>
<Icon icon="mdi:download-outline" class="text-base" aria-hidden="true" />
Download
<Icon icon="mdi:download-outline" class="text-xl" aria-hidden="true" />
</a>
</div>
{#if withUrl.length > 1}
@@ -263,7 +266,7 @@
type="button"
class="absolute -top-2 -right-2 inline-flex h-8 w-8 items-center justify-center rounded-full bg-white text-stein-900 shadow-md hover:bg-stein-100 focus:outline-none focus:ring-2 focus:ring-white/50"
onclick={closeModal}
aria-label="Schließen"
aria-label={t(T.image_gallery_close)}
>
<Icon icon="mdi:close" class="text-lg" aria-hidden="true" />
</button>