Hero: Figur unter den Header, Kopfabschnitt auf Mobil und Marquee-Sprung

Drei Fehler an der Live-Seite:

1. Die Figur begann bei top-0 und ragte damit in die Navigationsleiste -
   Kopf und Schulter lagen hinter "Start" und "Ueber mich". Sie startet
   jetzt bei top-16 bzw. top-20, also genau unter dem Header.

2. Auf schmalen Screens war der Kopf komplett abgeschnitten. Das Bild
   skalierte ueber die Breite (h-auto w-full); bei 72 % Breite wird das
   hochformatige Motiv hoeher als sein Container, und items-end schneidet
   dann oben ab. Es skaliert jetzt durchgehend ueber die Hoehe
   (h-full w-auto object-bottom), damit passt es immer hinein.

3. Das Genre-Laufband sprang sichtbar zurueck. Zwei Kopien wurden um -50 %
   verschoben, aber eine Kopie ist auf breiten Bildschirmen schmaler als der
   Viewport - dadurch klaffte vor dem Neustart eine Luecke. Jetzt zwei Spuren
   mit min-w-full, beide um ihre volle eigene Breite (-100 %); Spur 2 steht
   beim Neustart exakt auf der Startposition von Spur 1. Das umgebende
   Element bekam ausserdem das fehlende overflow-hidden.

Nachgemessen bei 1265x720 und 375x812: Kopf unterhalb der Headerkante,
beide Spuren gleich breit und mindestens viewportbreit, kein horizontaler
Scroll.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 23:35:44 +02:00
co-authored by Claude Opus 5
parent de7e8e3c37
commit fb10b9d94c
2 changed files with 23 additions and 7 deletions
+20 -6
View File
@@ -50,12 +50,17 @@ const genreLine = GENRES.map((g) => g.name).join(' • ');
Die Figur liegt bewusst absolut und nicht im Textfluss: als Grid-Spalte Die Figur liegt bewusst absolut und nicht im Textfluss: als Grid-Spalte
wuerde ihre Eigenhoehe (649x1919 px) die Zeile auf ueber 1200 px aufziehen wuerde ihre Eigenhoehe (649x1919 px) die Zeile auf ueber 1200 px aufziehen
und Ueberschrift samt Buttons unter die Falz druecken. und Ueberschrift samt Buttons unter die Falz druecken.
Sie skaliert ueber die Hoehe (h-full w-auto), nicht ueber die Breite -
sonst wird das hochformatige Bild auf schmalen Screens hoeher als der
Container und items-end schneidet oben den Kopf ab. top-16/top-20
entspricht der Headerhoehe, damit sie nicht in die Navigation ragt.
*/} */}
<div <div
class="pointer-events-none absolute top-0 right-[-8%] bottom-12 -z-5 flex w-[72%] items-end opacity-25 sm:right-0 sm:w-[52%] sm:opacity-35 lg:right-[4%] lg:bottom-14 lg:z-0 lg:w-auto lg:opacity-100" class="pointer-events-none absolute top-16 right-[-8%] bottom-12 -z-5 flex w-[72%] items-end justify-end opacity-25 sm:top-20 sm:right-0 sm:w-[52%] sm:opacity-35 lg:right-[4%] lg:bottom-14 lg:z-0 lg:w-auto lg:opacity-100"
aria-hidden="true" aria-hidden="true"
> >
<div class="relative ml-auto w-full max-w-[26rem] lg:h-full lg:w-auto lg:max-w-none"> <div class="relative h-full">
<div <div
class="absolute inset-x-[-30%] bottom-[6%] -z-10 aspect-square rounded-full bg-[radial-gradient(closest-side,--alpha(var(--color-gold-400)/22%),transparent)] blur-2xl" class="absolute inset-x-[-30%] bottom-[6%] -z-10 aspect-square rounded-full bg-[radial-gradient(closest-side,--alpha(var(--color-gold-400)/22%),transparent)] blur-2xl"
> >
@@ -66,7 +71,7 @@ const genreLine = GENRES.map((g) => g.name).join(' • ');
widths={[420, 649]} widths={[420, 649]}
sizes="(min-width: 1024px) 26rem, 60vw" sizes="(min-width: 1024px) 26rem, 60vw"
loading="eager" loading="eager"
class="h-auto w-full object-contain drop-shadow-[0_25px_45px_rgba(0,0,0,0.55)] [mask-image:linear-gradient(to_top,transparent_0%,black_7%)] lg:h-full lg:w-auto" class="h-full w-auto max-w-none object-contain object-bottom drop-shadow-[0_25px_45px_rgba(0,0,0,0.55)] [mask-image:linear-gradient(to_top,transparent_0%,black_7%)]"
/> />
</div> </div>
</div> </div>
@@ -129,13 +134,22 @@ const genreLine = GENRES.map((g) => g.name).join(' • ');
{/* Genre-Laufband */} {/* Genre-Laufband */}
<div <div
class="absolute inset-x-0 bottom-0 z-10 border-y border-white/10 bg-ink-950/60 py-3.5 backdrop-blur-md" class="absolute inset-x-0 bottom-0 z-10 overflow-hidden border-y border-white/10 bg-ink-950/60 py-3.5 backdrop-blur-md"
aria-hidden="true" aria-hidden="true"
> >
<div class="flex w-max animate-marquee"> {/*
Zwei identische Spuren, beide gleichzeitig um ihre volle eigene Breite
nach links. Sobald Spur 1 draussen ist, steht Spur 2 exakt an deren
Startposition - der Neustart faellt nicht auf.
min-w-full ist der entscheidende Teil: ohne das ist eine Spur auf breiten
Bildschirmen schmaler als der Viewport, es klafft eine Luecke und das Band
springt sichtbar zurueck.
*/}
<div class="flex">
{ {
[0, 1].map(() => ( [0, 1].map(() => (
<div class="flex shrink-0 items-center"> <div class="flex min-w-full shrink-0 animate-marquee items-center justify-around">
{GENRES.map((g) => ( {GENRES.map((g) => (
<span class="flex items-center"> <span class="flex items-center">
<span class="px-6 font-display text-sm tracking-[0.18em] text-white/45 uppercase sm:text-base"> <span class="px-6 font-display text-sm tracking-[0.18em] text-white/45 uppercase sm:text-base">
+3 -1
View File
@@ -30,9 +30,11 @@
--animate-cue: cue 2.2s ease-in-out infinite; --animate-cue: cue 2.2s ease-in-out infinite;
} }
/* Jede Spur wandert um ihre eigene volle Breite. Zwei Spuren nebeneinander
ergeben daraus einen nahtlosen Lauf - siehe Hero.astro. */
@keyframes marquee { @keyframes marquee {
from { transform: translateX(0); } from { transform: translateX(0); }
to { transform: translateX(-50%); } to { transform: translateX(-100%); }
} }
@keyframes sweep { @keyframes sweep {