homepage stuff and text animation fix

This commit is contained in:
elvira
2026-08-22 21:59:17 +03:00
parent 7677d10821
commit 45057f9083
6 changed files with 428 additions and 305 deletions
Generated
+324 -238
View File
File diff suppressed because it is too large Load Diff
+43 -22
View File
@@ -36,6 +36,13 @@ @layer base {
color: #000;
-webkit-font-smoothing: antialiased;
}
/* Browsers don't give <button> a pointer cursor by default (unlike <a>),
and Tailwind's Preflight stopped forcing it a few versions back — so
every button sitewide needs it set explicitly, once, here. */
button:not(:disabled) {
cursor: pointer;
}
}
@keyframes back-to-top-spin {
@@ -47,15 +54,6 @@ @keyframes back-to-top-spin {
}
}
@keyframes text-flicker-in {
0%, 60% {
opacity: 0;
}
70%, 100% {
opacity: 1;
}
}
/* ═══════════════════════════════════════════════════════════════════
COMPONENTS
═══════════════════════════════════════════════════════════════════ */
@@ -302,25 +300,48 @@ @layer components {
transform: translate(0, 0);
}
/* ── Text flicker — word switches from regular to italic/bold once
its .is-visible ancestor appears; add data-text="<same word>" ── */
.text-flicker {
position: relative;
/* ── Text dance — the word itself toggles font-weight/font-style
(normal → bold+italic) three times, then stays bold+italic, once
its .is-visible ancestor appears (pair with the `appear` Stimulus
controller — data-controller="appear" data-appear-visible-class
="is-visible"); add data-text="<same word>" ── */
.text-dance {
display: inline-block;
font-weight: inherit;
font-style: normal;
}
.text-flicker::after {
/* Layout-shift guard: reserve the wider bold+italic width up front.
A zero-height, invisible line contributes its (wider)
shrink-to-fit width to the inline-block box, so the box never
has to grow once the animation starts — nothing around the word
moves. Trade-off: since the real word starts out narrower and
left-aligned, this leaves a small trailing gap after it until
the first bold+italic pulse fills the box. (A variable-font
weight axis would be a second way to smooth this out, but
Manrope ships here as separate static weight files, not a
variable font, so there's no axis to animate.) */
.text-dance::before {
content: attr(data-text);
position: absolute;
inset: 0;
font-weight: 700;
display: block;
height: 0;
overflow: hidden;
visibility: hidden;
font-weight: 800;
font-style: italic;
font-weight: extra-bold;
opacity: 0;
pointer-events: none;
}
.is-visible .text-flicker::after {
animation: text-flicker-in 0.7s ease 3 forwards;
.is-visible .text-dance {
animation: text-dance 0.8s ease 3 forwards;
animation-delay: var(--dance-delay, 0s);
}
}
/* Replayed 3 times via animation-iteration-count on .is-visible
.text-dance above — each iteration starts back at 0% (normal) and
snaps to 100% (bold+italic); forwards fill holds the final
iteration's 100% state after it ends, so the word settles there. */
@keyframes text-dance {
0%, 60% { font-weight: inherit; font-style: normal; }
70%, 100% { font-weight: 800; font-style: italic; }
}
@@ -11,9 +11,9 @@ class="text-h2 leading-tight"
data-controller="appear"
data-appear-visible-class="is-visible"
>
<span class="text-flicker" data-text="Γράψου">Γράψου</span>
<span class="text-dance" data-text="Γράψου">Γράψου</span>
στο
<span class="text-flicker" data-text="newsletter">newsletter</span>
<span class="text-dance [--dance-delay:200ms]" data-text="newsletter">newsletter</span>
μας
</h2>
+13 -5
View File
@@ -1,8 +1,9 @@
@props([
'tag' => 'button',
'href' => null,
'type' => 'button',
'size' => 'lg',
'tag' => 'button',
'href' => null,
'type' => 'button',
'size' => 'lg',
'position' => 'relative',
])
@php
@@ -14,7 +15,14 @@
default => 'py-5 px-[46px] text-[19px]',
};
$class = 'btn-primary relative isolate inline-flex items-center justify-center border border-black font-display font-bold italic text-black cursor-pointer no-underline uppercase ' . $sizeClasses;
// $position defaults to 'relative' (needed so the ::before/::after layers
// in .btn-primary position against the button itself), but callers that
// need to place the button absolutely (e.g. a hover-reveal CTA over a
// product image) must pass position="absolute" here rather than adding
// "absolute" via the class prop — Tailwind's generated stylesheet always
// orders the "relative" utility after "absolute", so on a class clash
// "relative" silently wins and the button never actually gets positioned.
$class = 'btn-primary '.$position.' isolate inline-flex items-center justify-center border border-black font-display font-bold italic text-black cursor-pointer no-underline uppercase ' . $sizeClasses;
$attrs = $href
? $attributes->merge(['href' => $href, 'class' => $class])
@@ -5,24 +5,26 @@
'href' => '#',
])
<div class="group relative">
<a href="{{ $href }}" class="block border border-black overflow-hidden bg-white mb-4 relative -z-1">
@if($image)
<img
src="{{ $image }}"
alt="{{ $name }}"
loading="lazy"
class="w-full h-auto block"
>
@else
<div class="w-full aspect-square bg-neutral-300 flex items-center justify-center text-neutral-500 text-sm">
Χωρίς εικόνα
</div>
@endif
</a>
<div class="group">
<div class="relative flex items-center justify-center mb-4">
<a href="{{ $href }}" class="block w-full border border-black overflow-hidden bg-white">
@if($image)
<img
src="{{ $image }}"
alt="{{ $name }}"
loading="lazy"
class="w-full h-auto block"
>
@else
<div class="w-full aspect-square bg-neutral-300 flex items-center justify-center text-neutral-500 text-sm">
Χωρίς εικόνα
</div>
@endif
</a>
<div class="absolute inset-x-0 top-0 bottom-[calc(theme(spacing.4)+1.5rem+0.25rem)] flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-100 ">
<x-ui.button size="md">Προσθήκη στο καλάθι</x-ui.button>
<x-ui.button size="md" position="absolute" class="opacity-0 group-hover:opacity-100 transition-opacity duration-100">
Προσθήκη στο καλάθι
</x-ui.button>
</div>
<div class="flex items-baseline justify-between gap-4">
+27 -21
View File
@@ -15,13 +15,13 @@
</svg>
<h1
class="font-display font-semibold text-6xl lg:text-7xl leading-tight"
class="font-display font-semibold text-6xl lg:text-[80px] leading-tight"
aria-label="Ό,τι φαντάζεσαι, το τυπώνουμε"
data-controller="appear"
data-appear-visible-class="is-visible"
>
Ό,τι <span class="text-flicker" data-text="φαντάζεσαι">φαντάζεσαι</span>,<br>
το <span class="text-flicker" data-text="τυπώνουμε">τυπώνουμε</span>
Ό,τι <span class="text-dance" data-text="φαντάζεσαι">φαντάζεσαι</span>,<br>
το <span class="text-dance [--dance-delay:200ms]" data-text="τυπώνουμε">τυπώνουμε</span>
</h1>
</div>
@@ -31,20 +31,19 @@ class="font-display font-semibold text-6xl lg:text-7xl leading-tight"
type="button"
data-action="carousel#prev"
aria-label="Προηγούμενο προϊόν"
class="absolute left-4 lg:left-8 top-1/2 -translate-y-1/2 hover:opacity-60 transition-opacity"
class="absolute left-4 lg:left-8 top-1/2 z-10 -translate-y-1/2 hover:-translate-x-1 transition duration-500"
>
<x-ui.icon name="arrow-left" :size="62" class="[&_path]:stroke-2" />
</button>
<div class="w-full ">
<div class="w-full h-full">
@foreach($heroProducts as $product)
<a
href="{{ $product['href'] }}"
<div
data-carousel-target="slide"
class="{{ $loop->first ? '' : 'hidden' }} block"
class="{{ $loop->first ? '' : 'hidden' }} group h-full flex flex-col justify-between"
>
<div class="flex items-center justify-center">
<div class="border border-black bg-white mb-16 flex items-center justify-center overflow-hidden max-w-sm">
<div class="relative flex items-center justify-center">
<a href="{{ $product['href'] }}" class="block border border-black bg-white flex items-center justify-center overflow-hidden max-w-sm">
@if($product['image'])
<img
src="{{ $product['image'] }}"
@@ -58,15 +57,22 @@ class="w-full h-full object-cover"
@else
<span class="text-neutral-500 text-sm">Χωρίς εικόνα</span>
@endif
</div>
</a>
<x-ui.button size="md" position="absolute" class="opacity-0 group-hover:opacity-100 transition-opacity duration-100">
Προσθήκη στο καλάθι
</x-ui.button>
</div>
<div class="flex items-baseline justify-between gap-4">
<span class="font-display font-bold text-2xl">{{ $product['name'] }}</span>
@if($product['price'] !== null)
<span class="font-bold text-xl shrink-0"><x-ui.price :amount="$product['price']" /></span>
@endif
</div>
</a>
<div class="flex items-baseline justify-between gap-4">
<a href="{{ $product['href'] }}" class="font-display font-bold text-2xl leading-snug hover:text-brand">
{{ $product['name'] }}
</a>
@if($product['price'] !== null)
<span class="font-bold text-xl shrink-0"><x-ui.price :amount="$product['price']" /></span>
@endif
</div>
</div>
@endforeach
</div>
@@ -74,7 +80,7 @@ class="w-full h-full object-cover"
type="button"
data-action="carousel#next"
aria-label="Επόμενο προϊόν"
class="absolute right-4 lg:right-8 top-1/2 -translate-y-1/2 hover:opacity-60 transition-opacity"
class="absolute right-4 lg:right-8 top-1/2 z-10 -translate-y-1/2 hover:translate-x-1 transition duration-500"
>
<x-ui.icon name="arrow-right" :size="62" class="[&_path]:stroke-2"/>
</button>
@@ -117,7 +123,7 @@ class="absolute right-4 lg:right-8 top-1/2 -translate-y-1/2 hover:opacity-60 tra
</div>
<div class="flex items-center gap-6 px-8 py-12">
<button type="button" data-action="carousel#prev" aria-label="Προηγούμενα προϊόντα" class="shrink-0 hover:opacity-60 transition-opacity">
<button type="button" data-action="carousel#prev" aria-label="Προηγούμενα προϊόντα" class="shrink-0 hover:-translate-x-0.5 hover:opacity-60 transition duration-500">
<x-ui.icon name="arrow-left" :size="32" />
</button>
@@ -138,7 +144,7 @@ class="absolute right-4 lg:right-8 top-1/2 -translate-y-1/2 hover:opacity-60 tra
@endforelse
</div>
<button type="button" data-action="carousel#next" aria-label="Επόμενα προϊόντα" class="shrink-0 hover:opacity-60 transition-opacity">
<button type="button" data-action="carousel#next" aria-label="Επόμενα προϊόντα" class="shrink-0 hover:translate-x-0.5 hover:opacity-60 transition duration-500">
<x-ui.icon name="arrow-right" :size="32" />
</button>
</div>