Skip to content

Commit

Permalink
feat: refonte landing page
Browse files Browse the repository at this point in the history
  • Loading branch information
mtlaso committed Jan 19, 2025
1 parent 5f6c5f0 commit eae4a93
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 89 deletions.
203 changes: 114 additions & 89 deletions src/app/[locale]/(landing)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SPACING } from "@/app/[locale]/ui/spacing";
import BlurFade from "@/components/ui/blur-fade";
import { Button } from "@/components/ui/button";
import {
Card,
Expand All @@ -14,14 +15,30 @@ import {
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
import { ShimmerButton } from "@/components/ui/shimmer-button";
import { TextAnimate } from "@/components/ui/text-animate";
import { Link } from "@/i18n/routing";
import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
import { TbArchive, TbMail, TbRss } from "react-icons/tb";

export default function Home(): React.JSX.Element {
const t = useTranslations("metadata");
const elements: {
el: () => React.JSX.Element;
index: number;
}[] = [
{
el: HeroSection,
index: 0,
},
{
el: FeaturesSection,
index: 1,
},
{
el: CtaSection,
index: 2,
},
];

return (
<>
Expand Down Expand Up @@ -50,94 +67,11 @@ export default function Home(): React.JSX.Element {
SPACING.LG,
)}
>
{/* hero */}
<section className={cn("text-center", SPACING.MD)}>
<TextAnimate
className="text-4xl md:text-6xl font-extrabold leading-none tracking-tight"
animation="blurInUp"
by="line"
as={"h1"}
once={true}
>
{t("headline")}
</TextAnimate>

<TextAnimate
animation="fadeIn"
by="line"
className="text-xl"
as={"p"}
once={true}
>
{t("description")}
</TextAnimate>

<div className="flex justify-center">
<Link href="/login">
<ShimmerButton className="shadow-2xl h-12 px-12">
{t("ctaButton")}
</ShimmerButton>
</Link>
</div>
</section>

{/* features */}
<section
className={cn(
"grid grid-cols-1 md:grid-cols-3 grid-rows-1 md:grid-rows-2 gap-6 break-words",
)}
>
{t.raw("features")?.map(
(feature: {
key: string;
title: string;
description: string;
}) => (
<Card
className={cn(
"hover:shadow-md transition-all duration-200 gri grid-rows-subgri col-span1 gridcols-subgrid",
{
"md:row-start-1 md:col-span-2": feature.key === "0",
"md:row-start-1 md:row-span-2": feature.key === "1",
"md:row-start-2 md:col-span-2": feature.key === "2",
},
)}
key={feature.key}
>
<CardHeader>
<CardTitle
className={cn(
"font-extrabold text-xl md:text-2xl",
SPACING.SM,
)}
>
<div>
{feature.key === "0" && <TbArchive />}
{feature.key === "1" && <TbRss />}
{feature.key === "2" && <TbMail />}
</div>
<div>{feature.title}</div>
</CardTitle>
<CardDescription>{feature.description}</CardDescription>
</CardHeader>
</Card>
),
)}
</section>

{/* cta */}
<section className={cn("text-center", SPACING.MD)}>
<h2 className="text-3xl md:text-4xl font-bold leading-none tracking-tight">
{t("lastCtaHeadline")}
</h2>

<p className="text-lg">{t("lastCtaDescription")}</p>
<div>
<Link href="/login">
<Button size={"lg"}>{t("ctaButton")}</Button>
</Link>
</div>
</section>
{elements.map((el) => (
<BlurFade key={el.index} delay={0.25 + el.index * 0.05}>
{el.el()}
</BlurFade>
))}
</main>

<footer className="bg-secondary p-10">
Expand All @@ -164,3 +98,94 @@ export default function Home(): React.JSX.Element {
</>
);
}

const HeroSection = (): React.JSX.Element => {
const t = useTranslations("metadata");
return (
<section className={cn("text-center", SPACING.MD)}>
<h1 className="text-4xl md:text-6xl font-extrabold leading-none tracking-tight">
{t("headline")}
</h1>

<p className="text-xl">{t("description")}</p>

<div className="flex justify-center">
<Link href="/login">
<ShimmerButton className="shadow-2xl h-12 px-12">
{t("ctaButton")}
</ShimmerButton>
</Link>
</div>
</section>
);
};

const FeaturesSection = (): React.JSX.Element => {
const t = useTranslations("metadata");

return (
<section
className={cn(
"grid grid-cols-1 md:grid-cols-3 grid-rows-1 md:grid-rows-2 gap-6 break-words",
)}
>
{t.raw("features")?.map(
(feature: {
key: string;
title: string;
description: string;
}) => (
<Card
className={cn(
"hover:shadow-md transition-all duration-200 gri grid-rows-subgri col-span1 gridcols-subgrid",
{
"md:row-start-1 md:col-span-2": feature.key === "0",
"md:row-start-1 md:row-span-2": feature.key === "1",
"md:row-start-2 md:col-span-2": feature.key === "2",
},
)}
key={feature.key}
>
<CardHeader>
<CardTitle
className={cn("font-extrabold text-xl md:text-2xl", SPACING.SM)}
>
<div>
{feature.key === "0" && (
<TbArchive className="hover:text-primary" />
)}
{feature.key === "1" && (
<TbRss className="hover:text-primary" />
)}
{feature.key === "2" && (
<TbMail className="hover:text-primary transition-all duration-200" />
)}
</div>
<div>{feature.title}</div>
</CardTitle>
<CardDescription>{feature.description}</CardDescription>
</CardHeader>
</Card>
),
)}
</section>
);
};

const CtaSection = (): React.JSX.Element => {
const t = useTranslations("metadata");
return (
<section className={cn("text-center", SPACING.MD)}>
<h2 className="text-3xl md:text-4xl font-bold leading-none tracking-tight">
{t("lastCtaHeadline")}
</h2>

<p className="text-lg">{t("lastCtaDescription")}</p>
<div>
<Link href="/login">
<Button size={"lg"}>{t("ctaButton")}</Button>
</Link>
</div>
</section>
);
};
78 changes: 78 additions & 0 deletions src/components/ui/blur-fade.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client";

import {
AnimatePresence,
type UseInViewOptions,
type Variants,
motion,
useInView,
} from "motion/react";
import { useRef } from "react";

type MarginType = UseInViewOptions["margin"];

interface BlurFadeProps {
children: React.ReactNode;
className?: string;
variant?: {
hidden: { y: number };
visible: { y: number };
};
duration?: number;
delay?: number;
offset?: number;
direction?: "up" | "down" | "left" | "right";
inView?: boolean;
inViewMargin?: MarginType;
blur?: string;
}

export default function BlurFade({
children,
className,
variant,
duration = 0.4,
delay = 0,
offset = 6,
direction = "down",
inView = false,
inViewMargin = "-50px",
blur = "6px",
}: BlurFadeProps): React.JSX.Element {
const ref = useRef(null);
const inViewResult = useInView(ref, { once: true, margin: inViewMargin });
const isInView = !inView || inViewResult;
const defaultVariants: Variants = {
hidden: {
[direction === "left" || direction === "right" ? "x" : "y"]:
direction === "right" || direction === "down" ? -offset : offset,
opacity: 0,
filter: `blur(${blur})`,
},
visible: {
[direction === "left" || direction === "right" ? "x" : "y"]: 0,
opacity: 1,
filter: "blur(0px)",
},
};
const combinedVariants = variant || defaultVariants;
return (
<AnimatePresence>
<motion.div
ref={ref}
initial="hidden"
animate={isInView ? "visible" : "hidden"}
exit="hidden"
variants={combinedVariants}
transition={{
delay: 0.04 + delay,
duration,
ease: "easeOut",
}}
className={className}
>
{children}
</motion.div>
</AnimatePresence>
);
}

0 comments on commit eae4a93

Please sign in to comment.