Produktseiten: Zurück-Button oben, springt zur echten vorherigen Seite (alle 4 Sprachen)

Auf ausdrücklichen Wunsch: oben auf jeder Produktseite jetzt ein "← Zurück"-Button vor dem
Breadcrumb. Nutzt den Browser-Verlauf (history.back()) statt fest zur Kategorieseite zu
springen — landet man z.B. von der Startseite, einer Suche oder "Das könnte dir gefallen" auf
einer Produktseite, kommt man genau dahin zurück. Fällt sauber auf einen normalen Link zur
Kategorieseite zurück, wenn kein echter vorheriger Seitenaufruf im Verlauf existiert (Direkt-
aufruf, neuer Tab, Suchmaschine). In allen 4 Sprachversionen (de/en/ch/fr) umgesetzt.

Nebenbei behoben: t.common.skipToContent (Skip-Link aus der BFSG-Barrierefreiheits-Arbeit vom
04.08.) war nur für "de" gepflegt und dadurch auf den en/ch/fr-Seiten leer — jetzt für alle
4 Sprachen ergänzt (gleicher Commit, da hier ohnehin dieselbe common-Sektion bearbeitet wurde).

Lokal mit wrangler pages dev end-to-end getestet (Login, echtes Produkt, Button + Skript im
gerenderten HTML bestätigt).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
qcigano
2026-08-04 14:22:20 +02:00
co-authored by Claude Sonnet 5
parent e37d12b4c7
commit 90dfca2d4f
6 changed files with 116 additions and 1 deletions
+4 -1
View File
@@ -30,7 +30,7 @@ export const ui = {
quantity: "Anzahl", photoSoon: "Produktfoto folgt", portraitSoon: "Porträtfoto von VanVan folgt",
prevPhoto: "Vorheriges Foto", nextPhoto: "Nächstes Foto",
handmadeBadge: "🩵 Handgemacht",
skipToContent: "Zum Inhalt springen",
skipToContent: "Zum Inhalt springen", back: "← Zurück",
},
product: {
articleNo: "Artikelnummer", material: "Material", size: "Größe", colors: "Farben",
@@ -348,6 +348,7 @@ export const ui = {
quantity: "Quantity", photoSoon: "Product photo coming soon", portraitSoon: "VanVan's portrait photo coming soon",
prevPhoto: "Previous photo", nextPhoto: "Next photo",
handmadeBadge: "🩵 Handmade",
skipToContent: "Skip to content", back: "← Back",
},
product: {
articleNo: "Item number", material: "Material", size: "Size", colors: "Colors",
@@ -665,6 +666,7 @@ export const ui = {
quantity: "Aazahl", photoSoon: "Produktfoto chunnt bald", portraitSoon: "Porträtfoto vo VanVan chunnt bald",
prevPhoto: "Vorherigs Foto", nextPhoto: "Nächschts Foto",
handmadeBadge: "🩵 Vo Hang gmacht",
skipToContent: "Zum Inhalt springe", back: "← Zrugg",
},
product: {
articleNo: "Artikelnummer", material: "Material", size: "Grösse", colors: "Farbe",
@@ -982,6 +984,7 @@ export const ui = {
quantity: "Quantité", photoSoon: "Photo du produit à venir", portraitSoon: "Photo de VanVan à venir",
prevPhoto: "Photo précédente", nextPhoto: "Photo suivante",
handmadeBadge: "🩵 Fait main",
skipToContent: "Aller au contenu", back: "← Retour",
},
product: {
articleNo: "Référence", material: "Matière", size: "Taille", colors: "Couleurs",
+21
View File
@@ -44,6 +44,9 @@ const felder: [string, string | undefined][] = [
<Layout title={name} description={desc} lang={lang} path={`/produkt/${product.slug}/`}>
<section class="section-tight">
<div class="container">
<div class="product-back-row">
<a href={`/ch/shop/${product.kategorie}/`} class="product-back-btn" id="product-back-btn">{t.common.back}</a>
</div>
<p class="small breadcrumb">
<a href="/ch/shop/">{t.shop.breadcrumbShop}</a> / <a href={`/ch/shop/${product.kategorie}/`}>{category?.name[lang]}</a> / <strong>{name}</strong>
</p>
@@ -194,6 +197,24 @@ const felder: [string, string | undefined][] = [
)}
</Layout>
<script>
// Zrugg-Button obe uf de Produktsyte: springt zur würklich vorherige Syte im Browser-Verlauf
// (z.B. Shop-Grid mit vorheriger Scroll-Position, Suechergebnis, Startsyte …), statt immer fescht
// zur Kategorie z'gah. Nur wenn es en echte, gliich-originäre vorherige Syteufruef git
// (document.referrer) — susch bliibt de normal Link zur Kategoriesyte als sinnvolli Rückfallebeni.
const backBtn = document.getElementById("product-back-btn");
backBtn?.addEventListener("click", (e) => {
if (!document.referrer || window.history.length <= 1) return;
try {
if (new URL(document.referrer).origin === location.origin) {
e.preventDefault();
window.history.back();
}
} catch {
// Kaputti/fremdi Referrer-URL — eifach bim normale Link zur Kategoriesyte blyybe.
}
});
</script>
<script define:vars={{ onlyLeftTemplate: t.common.onlyXLeft("__N__"), maxInCartText: t.common.maxInCart, maxInCartShortText: t.common.maxInCartShort, soldOutText: t.common.soldOut, addToCartText: t.common.addToCart }}>
window.__productStockVars = { onlyLeftTemplate, maxInCartText, maxInCartShortText, soldOutText, addToCartText };
</script>
+22
View File
@@ -44,6 +44,9 @@ const felder: [string, string | undefined][] = [
<Layout title={name} description={desc} lang={lang} path={`/produkt/${product.slug}/`}>
<section class="section-tight">
<div class="container">
<div class="product-back-row">
<a href={`/en/shop/${product.kategorie}/`} class="product-back-btn" id="product-back-btn">{t.common.back}</a>
</div>
<p class="small breadcrumb">
<a href="/en/shop/">{t.shop.breadcrumbShop}</a> / <a href={`/en/shop/${product.kategorie}/`}>{category?.name[lang]}</a> / <strong>{name}</strong>
</p>
@@ -194,6 +197,25 @@ const felder: [string, string | undefined][] = [
)}
</Layout>
<script>
// Back button at the top of the product page: jumps to the actually previous page in the
// browser history (shop grid with its scroll position, search result, homepage, …) instead of
// always going to the category page. Only when a real same-origin previous page exists
// (document.referrer) — otherwise the normal link to the category page stays as a sensible
// fallback (e.g. direct visit, new tab, coming from a search engine).
const backBtn = document.getElementById("product-back-btn");
backBtn?.addEventListener("click", (e) => {
if (!document.referrer || window.history.length <= 1) return;
try {
if (new URL(document.referrer).origin === location.origin) {
e.preventDefault();
window.history.back();
}
} catch {
// Broken/foreign referrer URL — just keep the normal link to the category page.
}
});
</script>
<script define:vars={{ onlyLeftTemplate: t.common.onlyXLeft("__N__"), maxInCartText: t.common.maxInCart, maxInCartShortText: t.common.maxInCartShort, soldOutText: t.common.soldOut, addToCartText: t.common.addToCart }}>
window.__productStockVars = { onlyLeftTemplate, maxInCartText, maxInCartShortText, soldOutText, addToCartText };
</script>
+23
View File
@@ -44,6 +44,9 @@ const felder: [string, string | undefined][] = [
<Layout title={name} description={desc} lang={lang} path={`/produkt/${product.slug}/`}>
<section class="section-tight">
<div class="container">
<div class="product-back-row">
<a href={`/fr/shop/${product.kategorie}/`} class="product-back-btn" id="product-back-btn">{t.common.back}</a>
</div>
<p class="small breadcrumb">
<a href="/fr/shop/">{t.shop.breadcrumbShop}</a> / <a href={`/fr/shop/${product.kategorie}/`}>{category?.name[lang]}</a> / <strong>{name}</strong>
</p>
@@ -194,6 +197,26 @@ const felder: [string, string | undefined][] = [
)}
</Layout>
<script>
// Bouton retour en haut de la page produit : revient à la page VRAIMENT précédente dans
// l'historique du navigateur (grille boutique avec sa position de défilement, résultat de
// recherche, accueil…) plutôt que d'aller systématiquement à la page de catégorie. Uniquement
// si une page précédente de même origine existe réellement (document.referrer) — sinon le lien
// normal vers la page de catégorie reste un repli pertinent (visite directe, nouvel onglet,
// provenance d'un moteur de recherche).
const backBtn = document.getElementById("product-back-btn");
backBtn?.addEventListener("click", (e) => {
if (!document.referrer || window.history.length <= 1) return;
try {
if (new URL(document.referrer).origin === location.origin) {
e.preventDefault();
window.history.back();
}
} catch {
// URL de provenance cassée/étrangère — on garde simplement le lien normal vers la catégorie.
}
});
</script>
<script define:vars={{ onlyLeftTemplate: t.common.onlyXLeft("__N__"), maxInCartText: t.common.maxInCart, maxInCartShortText: t.common.maxInCartShort, soldOutText: t.common.soldOut, addToCartText: t.common.addToCart }}>
window.__productStockVars = { onlyLeftTemplate, maxInCartText, maxInCartShortText, soldOutText, addToCartText };
</script>
+28
View File
@@ -44,6 +44,15 @@ const felder: [string, string | undefined][] = [
<Layout title={name} description={desc} lang={lang} path={`/produkt/${product.slug}/`}>
<section class="section-tight">
<div class="container">
{/* Auf ausdrücklichen Wunsch: oben auf jeder Produktseite ein Zurück-Button, der wirklich
zur vorherigen Seite zurückspringt (Browser-Verlauf), statt immer fest zur Kategorie zu
gehen — falls man z.B. von der Startseite, einer Suche oder "Das könnte dir gefallen"
kam. href bleibt als Rückfallebene gesetzt (kein JS / neuer Tab ohne Verlauf), das
Skript unten überschreibt den Klick nur, wenn ein echter vorheriger Seitenaufruf
derselben Seite im Verlauf existiert. */}
<div class="product-back-row">
<a href={`/shop/${product.kategorie}/`} class="product-back-btn" id="product-back-btn">{t.common.back}</a>
</div>
<p class="small breadcrumb">
<a href="/shop/">{t.shop.breadcrumbShop}</a> / <a href={`/shop/${product.kategorie}/`}>{category?.name[lang]}</a> / <strong>{name}</strong>
</p>
@@ -194,6 +203,25 @@ const felder: [string, string | undefined][] = [
)}
</Layout>
<script>
// Zurück-Button oben auf der Produktseite: springt zur WIRKLICH vorherigen Seite im
// Browser-Verlauf (z.B. Shop-Grid mit vorheriger Scroll-Position, Suchergebnis, Startseite …),
// statt immer stur zur Kategorieseite zu gehen. Nur wenn ein echter, gleich-originärer voriger
// Seitenaufruf existiert (document.referrer) — sonst bleibt der normale Link zur Kategorieseite
// als sinnvoller Rückfall aktiv (z.B. Direktaufruf, neuer Tab, von einer Suchmaschine).
const backBtn = document.getElementById("product-back-btn");
backBtn?.addEventListener("click", (e) => {
if (!document.referrer || window.history.length <= 1) return;
try {
if (new URL(document.referrer).origin === location.origin) {
e.preventDefault();
window.history.back();
}
} catch {
// Kaputte/fremde Referrer-URL — einfach beim normalen Link zur Kategorieseite bleiben.
}
});
</script>
<script define:vars={{ onlyLeftTemplate: t.common.onlyXLeft("__N__"), maxInCartText: t.common.maxInCart, maxInCartShortText: t.common.maxInCartShort, soldOutText: t.common.soldOut, addToCartText: t.common.addToCart }}>
window.__productStockVars = { onlyLeftTemplate, maxInCartText, maxInCartShortText, soldOutText, addToCartText };
</script>
+18
View File
@@ -1122,6 +1122,24 @@ a:focus-visible, button:focus-visible {
/* Aktuelle Ebene (Haupt-/Unterkategorie, Produktname) hebt sich etwas vom "Shop"-Wurzel-Link ab. */
.breadcrumb strong { color: var(--c-text); font-weight: 700; }
/* Zurück-Button oben auf der Produktseite (siehe src/pages/produkt/[slug].astro) — springt per
Skript zur wirklich vorherigen Seite im Browser-Verlauf, der href hier ist die Rückfallebene. */
.product-back-row { margin-bottom: 0.9rem; }
.product-back-btn {
display: inline-flex;
align-items: center;
gap: 0.4em;
padding: 0.4em 0.95em;
border-radius: 999px;
border: 1px solid rgba(244, 241, 247, 0.16);
background: rgba(244, 241, 247, 0.04);
color: var(--c-text-muted);
font-size: 0.86rem;
font-weight: 600;
transition: border-color 0.2s var(--ease), color 0.2s var(--ease), background 0.2s var(--ease), transform 0.2s var(--ease);
}
.product-back-btn:hover { border-color: var(--c-accent); color: var(--c-accent); transform: translateX(-2px); }
/* Überschrift über der "weitere Produkte"-Reihe auf Unterkategorie-Seiten — macht klar, dass da
noch mehr Produkte derselben Reihe kommen (Charakter-Familie), egal wie viele es am Ende sind. */
.more-variants-label { text-transform: uppercase; letter-spacing: 0.08em; color: var(--c-text-muted); margin-bottom: 0.8rem; }