Skip to content

Commit 6553415

Browse files
committed
feat: animated missing sections
1 parent c6ca797 commit 6553415

4 files changed

Lines changed: 118 additions & 132 deletions

File tree

src/components/sections/BlogPreviewSection.tsx

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
import { StaggerContainer, StaggerItem } from "@/components/animations/Stagger"
12
import { Card } from "@/components/ui/Card"
23
import { Section } from "@/components/ui/Section"
3-
import { formatLongDate } from "@/lib/formatters"
44
import { getBlogPostBySlug, type BlogPreviewLayoutBlock } from "@/lib/cms"
5+
import { formatLongDate } from "@/lib/formatters"
56
import type { AppLocale } from "@/lib/i18n"
67
import { getMediaUrl } from "@/lib/media"
78
import { cn } from "@/lib/utils"
89
import type { Media } from "@/payload-types"
910
import Image from "next/image"
1011
import Link from "next/link"
1112
import type { ReactNode } from "react"
13+
import { OffsetCardMotion } from "./client/OffsetCardMotion"
1214

1315
interface BlogPreviewSectionProps {
1416
content?: BlogPreviewLayoutBlock | null
@@ -40,6 +42,7 @@ export async function BlogPreviewSection({ content, locale = "en" }: BlogPreview
4042
const isImageCenter = content.sectionLayout === "imageCenter"
4143
const isImageRight = content.sectionLayout === "imageRight"
4244
const isImageLeft = content.sectionLayout === "imageLeft"
45+
const motionPreset = isImageLeft ? "slide-right" : "slide-left"
4346
const postHref = `/${locale}/blog/${post.slug}`
4447
const contentSpacingClassName = isImageCenter
4548
? showBorder
@@ -51,31 +54,41 @@ export async function BlogPreviewSection({ content, locale = "en" }: BlogPreview
5154

5255
return (
5356
<Section heading={content.sectionHeading} description={content.sectionDescription} funnelType="center" animation={{ preset: "none" }}>
54-
<Link href={postHref} className={cn("group block", isImageCenter && "mx-auto w-full max-w-5xl")}>
55-
<PreviewFrame showBorder={showBorder}>
56-
<article className={cn("relative z-10 overflow-hidden", isImageCenter ? "flex flex-col" : "grid min-h-96 lg:grid-cols-2")}>
57-
<div className={cn("relative overflow-hidden bg-primary/40", isImageCenter ? "aspect-video" : "min-h-64 lg:min-h-96", isImageRight && "lg:order-2")}>
58-
{heroImageUrl ? (
59-
<Image
60-
src={heroImageUrl}
61-
alt={heroImage?.alt ?? post.title}
62-
fill
63-
sizes={isImageCenter ? "(min-width: 1024px) 960px, 100vw" : "(min-width: 1024px) 50vw, 100vw"}
64-
className="object-cover border border-white/5 rounded-2xl"
65-
/>
66-
) : (
67-
<div className="image-placeholder h-full min-h-64 w-full px-4 text-sm">{locale === "de" ? "Kein Bild" : "No image"}</div>
68-
)}
69-
</div>
57+
<OffsetCardMotion index={0} preset={motionPreset} className={cn(isImageCenter && "mx-auto w-full max-w-5xl")}>
58+
<Link href={postHref} className="group block">
59+
<PreviewFrame showBorder={showBorder}>
60+
<article className={cn("relative z-10 overflow-hidden", isImageCenter ? "flex flex-col" : "grid min-h-96 lg:grid-cols-2")}>
61+
<div className={cn("relative overflow-hidden bg-primary/40", isImageCenter ? "aspect-video" : "min-h-64 lg:min-h-96", isImageRight && "lg:order-2")}>
62+
{heroImageUrl ? (
63+
<Image
64+
src={heroImageUrl}
65+
alt={heroImage?.alt ?? post.title}
66+
fill
67+
sizes={isImageCenter ? "(min-width: 1024px) 960px, 100vw" : "(min-width: 1024px) 50vw, 100vw"}
68+
className="object-cover border border-white/5 rounded-2xl"
69+
/>
70+
) : (
71+
<div className="image-placeholder h-full min-h-64 w-full px-4 text-sm">{locale === "de" ? "Kein Bild" : "No image"}</div>
72+
)}
73+
</div>
7074

71-
<div className={cn("flex flex-col justify-center", contentSpacingClassName, isImageRight && "lg:order-1")}>
72-
<p className="mb-3 text-xs text-tertiary">{[authorName, publishedDate].filter(Boolean).join(" · ")}</p>
73-
<h3 className="max-w-3xl text-3xl font-semibold leading-tight tracking-tight text-white md:text-4xl">{post.title}</h3>
74-
{post.shortDescription && <p className="mt-4 max-w-2xl text-base leading-7 text-secondary md:text-lg">{post.shortDescription}</p>}
75-
</div>
76-
</article>
77-
</PreviewFrame>
78-
</Link>
75+
<StaggerContainer className={cn("flex flex-col justify-center", contentSpacingClassName, isImageRight && "lg:order-1")} delayChildren={0.06} staggerChildren={0.08}>
76+
<StaggerItem as="p" y={14} duration={0.38} className="mb-3 text-xs text-tertiary">
77+
{[authorName, publishedDate].filter(Boolean).join(" \u00b7 ")}
78+
</StaggerItem>
79+
<StaggerItem as="h2" y={14} duration={0.38} className="max-w-3xl text-3xl font-semibold leading-tight tracking-tight text-white md:text-4xl">
80+
{post.title}
81+
</StaggerItem>
82+
{post.shortDescription && (
83+
<StaggerItem as="p" y={14} duration={0.38} className="mt-4 max-w-2xl text-base leading-7 text-secondary md:text-lg">
84+
{post.shortDescription}
85+
</StaggerItem>
86+
)}
87+
</StaggerContainer>
88+
</article>
89+
</PreviewFrame>
90+
</Link>
91+
</OffsetCardMotion>
7992
</Section>
8093
)
8194
}

src/components/sections/ListFeatureSection.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import { StaggerContainer, StaggerItem } from "@/components/animations/Stagger"
2+
import { getIcon } from "@/components/IconRenderer"
13
import { Section } from "@/components/ui/Section"
24
import type { ListFeatureLayoutBlock } from "@/lib/cms"
3-
import { getIcon } from "@/components/IconRenderer"
45

56
interface ListFeatureSectionProps {
67
content?: ListFeatureLayoutBlock | null
@@ -19,18 +20,18 @@ export function ListFeatureSection({ content }: ListFeatureSectionProps) {
1920
funnelType={content.sectionLayout ?? "center"}
2021
animation={{ preset: "none" }}
2122
>
22-
<div className="grid gap-8 md:gap-12 md:grid-cols-2 lg:grid-cols-3">
23+
<StaggerContainer className="grid gap-8 md:gap-12 md:grid-cols-2 lg:grid-cols-3" delayChildren={0.04} staggerChildren={0.08}>
2324
{features.map((feature, index) => (
24-
<div key={feature.id ?? `${feature.title}-${index}`} className="flex flex-col gap-4">
25+
<StaggerItem key={feature.id ?? `${feature.title}-${index}`} y={14} duration={0.38} className="flex flex-col gap-4">
2526
<div className="flex size-12 items-center justify-center rounded-xl border border-white/5 bg-white/5 text-white">{getIcon(feature.icon, 24)}</div>
2627

2728
<div className="flex flex-col">
2829
<h3 className="text-lg font-semibold tracking-wide text-white">{feature.title}</h3>
2930
{feature.description && <p className="text-secondary">{feature.description}</p>}
3031
</div>
31-
</div>
32+
</StaggerItem>
3233
))}
33-
</div>
34+
</StaggerContainer>
3435
</Section>
3536
)
3637
}

src/components/sections/ScrollCardSection.tsx

Lines changed: 38 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { StaggerContainer, StaggerItem } from "@/components/animations/Stagger"
12
import { LinkButton } from "@/components/ui/LinkButton"
23
import { Section } from "@/components/ui/Section"
34
import type { ScrollCardsLayoutBlock } from "@/lib/cms"
@@ -16,6 +17,39 @@ function getImage(image: number | Media | null | undefined) {
1617
return typeof image === "object" ? image : null
1718
}
1819

20+
type ScrollCardItem = NonNullable<ScrollCardsLayoutBlock["items"]>[number]
21+
22+
function ScrollCardContent({ item, centered = false }: { item: ScrollCardItem; centered?: boolean }) {
23+
return (
24+
<StaggerContainer className={cn("flex flex-col gap-6", centered ? "items-center text-center" : "text-left")} delayChildren={0.04} staggerChildren={0.08}>
25+
<StaggerItem as="h2" y={14} duration={0.38} className={cn("text-3xl font-semibold text-white md:text-5xl", centered ? "max-w-4xl" : "max-w-xl")}>
26+
{item.title}
27+
</StaggerItem>
28+
29+
{item.description && (
30+
<StaggerItem as="p" y={14} duration={0.38} className={cn("text-base leading-7 text-secondary md:text-lg", centered ? "max-w-2xl" : "max-w-xl")}>
31+
{item.description}
32+
</StaggerItem>
33+
)}
34+
35+
<StaggerItem as="ul" y={14} duration={0.38} className="grid gap-2 text-sm text-secondary md:text-base">
36+
{item.bulletPoints?.map((point, pointIndex) => (
37+
<li key={`${item.id ?? item.title}-point-${pointIndex}`} className="flex items-start gap-3">
38+
<span className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-brand" />
39+
<span>{point}</span>
40+
</li>
41+
))}
42+
</StaggerItem>
43+
44+
{item.link?.label && item.link?.url && (
45+
<StaggerItem y={14} duration={0.38}>
46+
<LinkButton href={item.link.url}>{item.link.label}</LinkButton>
47+
</StaggerItem>
48+
)}
49+
</StaggerContainer>
50+
)
51+
}
52+
1953
export function ScrollCardSection({ content }: ScrollCardSectionProps) {
2054
const items = content?.items?.filter((item) => Boolean(item.title)) ?? []
2155
if (items.length === 0) return null
@@ -62,46 +96,15 @@ export function ScrollCardSection({ content }: ScrollCardSectionProps) {
6296
{imageUrl && <Image src={imageUrl} alt={image?.alt ?? item.title} fill sizes="100vw" className="object-cover object-center" />}
6397

6498
<div className="absolute inset-0 z-20 flex items-center justify-center">
65-
<div className="mx-auto flex max-w-4xl flex-col items-center gap-6 px-6 text-center">
66-
<h2 className="text-3xl font-semibold text-white md:text-5xl">{item.title}</h2>
67-
68-
{item.description && <p className="max-w-2xl text-base leading-7 text-secondary md:text-lg">{item.description}</p>}
69-
70-
<div className="space-y-6">
71-
<ul className="grid gap-2 text-sm text-secondary md:text-base">
72-
{item.bulletPoints?.map((point, pointIndex) => (
73-
<li key={`${item.id ?? item.title}-point-${pointIndex}`} className="flex items-start gap-3">
74-
<span className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-brand" />
75-
<span>{point}</span>
76-
</li>
77-
))}
78-
</ul>
79-
80-
{item.link?.label && item.link?.url && <LinkButton href={item.link.url}>{item.link.label}</LinkButton>}
81-
</div>
99+
<div className="mx-auto max-w-4xl px-6">
100+
<ScrollCardContent item={item} centered />
82101
</div>
83102
</div>
84103
</div>
85104
) : isSideFullscreen ? (
86105
<>
87106
<div className={cn("relative z-10 flex flex-col justify-center gap-6 p-6 sm:p-8 md:h-full md:gap-8 md:p-12", isSideFullscreenLeft && "md:order-2")}>
88-
<div className="flex flex-col gap-4">
89-
<h2 className="max-w-xl text-3xl font-semibold text-white md:text-5xl">{item.title}</h2>
90-
<p className="max-w-xl text-base leading-7 text-secondary md:text-lg">{item.description}</p>
91-
</div>
92-
93-
<div className="space-y-6">
94-
<ul className="grid gap-2 text-sm text-secondary md:text-base">
95-
{item.bulletPoints?.map((point, pointIndex) => (
96-
<li key={`${item.id ?? item.title}-point-${pointIndex}`} className="flex items-start gap-3">
97-
<span className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-brand" />
98-
<span>{point}</span>
99-
</li>
100-
))}
101-
</ul>
102-
103-
{item.link?.label && item.link?.url && <LinkButton href={item.link.url}>{item.link.label}</LinkButton>}
104-
</div>
107+
<ScrollCardContent item={item} />
105108
</div>
106109

107110
<div
@@ -118,23 +121,7 @@ export function ScrollCardSection({ content }: ScrollCardSectionProps) {
118121
) : (
119122
<>
120123
<div className={cn("relative z-10 flex h-full flex-col justify-center gap-8 rounded-3xl", isImageLeft && "md:order-2")}>
121-
<div className="flex flex-col gap-4">
122-
<h2 className="max-w-xl text-3xl font-semibold text-white md:text-5xl">{item.title}</h2>
123-
{item.description && <p className="max-w-xl text-base leading-7 text-secondary md:text-lg">{item.description}</p>}
124-
</div>
125-
126-
<div className="space-y-6">
127-
<ul className="grid gap-2 text-sm text-secondary md:text-base">
128-
{item.bulletPoints?.map((point, pointIndex) => (
129-
<li key={`${item.id ?? item.title}-point-${pointIndex}`} className="flex items-start gap-3">
130-
<span className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-brand" />
131-
<span>{point}</span>
132-
</li>
133-
))}
134-
</ul>
135-
136-
{item.link?.label && item.link?.url && <LinkButton href={item.link.url}>{item.link.label}</LinkButton>}
137-
</div>
124+
<ScrollCardContent item={item} />
138125
</div>
139126

140127
<div

0 commit comments

Comments
 (0)