diff --git a/app/[locale]/(site)/success/page.tsx b/app/[locale]/(site)/success/page.tsx index 91b63d8..a55d73b 100644 --- a/app/[locale]/(site)/success/page.tsx +++ b/app/[locale]/(site)/success/page.tsx @@ -5,6 +5,7 @@ import { getTranslations } from "next-intl/server"; import { Container } from "@/components/layout/container"; import { MotionFade } from "@/components/motion-fade"; +import { HeroShell, HeroTitle } from "@/components/layout/site-hero"; import { buildLocalizedMetadata } from "@/lib/metadata"; import { getLocalizedPath, resolveLocale } from "@/lib/locale"; import { AppCard } from "@/components/ui/app-card"; @@ -37,32 +38,28 @@ export default async function SuccessPage({ params: { locale } }: SuccessPagePro return ( <> -
- - -
- -

- {t("title")} -

-

- {t("title")} -

-

- {t("text")} -

-
- - -
+ + +
+ +

+ {t("title")} +

+ +

+ {t("text")} +

+
+ +
- - -
+ + + diff --git a/app/[locale]/coming-soon/page.tsx b/app/[locale]/coming-soon/page.tsx index 658a19e..12d130b 100644 --- a/app/[locale]/coming-soon/page.tsx +++ b/app/[locale]/coming-soon/page.tsx @@ -3,8 +3,8 @@ import { Sparkles } from "lucide-react"; import { getTranslations } from "next-intl/server"; import { FloatingPreferences } from "@/components/layout/floating-preferences"; -import { HeroMotionBackdrop } from "@/components/layout/hero-motion-backdrop"; import { MotionFade } from "@/components/motion-fade"; +import { HeroShell, HeroTitle } from "@/components/layout/site-hero"; import { getSiteSettings } from "@/lib/app-config"; import { buildLocalizedMetadata } from "@/lib/metadata"; import { resolveLocale } from "@/lib/locale"; @@ -37,52 +37,37 @@ export default async function ComingSoonPage({ const localeKey = resolveLocale(locale); const t = await getTranslations({ locale: localeKey, namespace: "comingSoon" }); const isArabic = localeKey === "ar"; + const lines = [ + { + text: t("titleLineOne"), + tone: "soft" as const, + align: isArabic ? "center" as const : "start" as const, + }, + { + text: t("titleLineTwo"), + tone: "default" as const, + align: isArabic ? "center" as const : "end" as const, + }, + { + text: t("titleLineThree"), + tone: "accent" as const, + align: isArabic ? "center" as const : "start" as const, + }, + ]; return (
-
- -
-
- +
-

+

{t("kicker")}

-

- - {t("titleLineOne")} - - - {t("titleLineTwo")} - - - {t("titleLineThree")} - -

+

{t("description")} @@ -93,7 +78,7 @@ export default async function ComingSoonPage({

-
+
); } diff --git a/components/layout/hero-motion-backdrop.tsx b/components/layout/hero-motion-backdrop.tsx index 18fe1c7..6fec029 100644 --- a/components/layout/hero-motion-backdrop.tsx +++ b/components/layout/hero-motion-backdrop.tsx @@ -30,7 +30,7 @@ export function HeroMotionBackdrop({ compact = false }: HeroMotionBackdropProps) scale: compact ? [1, 0.98, 1.04, 1] : [1, 0.94, 1.08, 1], }} transition={{ - duration: compact ? 20 : 30, + duration: compact ? 20 : 28, repeat: Number.POSITIVE_INFINITY, ease: "easeInOut", }} diff --git a/components/layout/home-hero.tsx b/components/layout/home-hero.tsx index 9e54706..8780ddf 100644 --- a/components/layout/home-hero.tsx +++ b/components/layout/home-hero.tsx @@ -1,6 +1,4 @@ -import { ArrowDown } from "lucide-react"; - -import { HeroMotionBackdrop } from "@/components/layout/hero-motion-backdrop"; +import { HeroScrollLink, HeroShell, HeroTitle } from "@/components/layout/site-hero"; type HomeHeroProps = { locale: string; @@ -17,53 +15,44 @@ export function HomeHero({ }: HomeHeroProps) { const titleLines = title.split("\n").filter(Boolean); const isArabic = locale === "ar"; + const lines = titleLines.map((line, index) => ({ + text: line, + tone: isArabic + ? index === 0 + ? "accent" + : "default" + : index === titleLines.length - 1 + ? "accent" + : index === 0 + ? "soft" + : "default", + align: isArabic + ? "center" + : index === 1 + ? "end" + : "start", + })) as { + text: string; + tone: "default" | "accent" | "soft"; + align: "start" | "center" | "end"; + }[]; return ( -
- -
-
-
-
-

- {kicker} -

-

- {titleLines.map((line, index) => ( - - {line} - - ))} -

-

- {description} -

-
- - - -
-
+ +

+ {kicker} +

+ +

+ {description} +

+
+
-
+ ); } diff --git a/components/layout/page-hero.tsx b/components/layout/page-hero.tsx index cd59fb5..54731f1 100644 --- a/components/layout/page-hero.tsx +++ b/components/layout/page-hero.tsx @@ -1,4 +1,4 @@ -import { HeroMotionBackdrop } from "@/components/layout/hero-motion-backdrop"; +import { HeroShell, HeroTitle } from "@/components/layout/site-hero"; type PageHeroProps = { title: string; @@ -7,23 +7,16 @@ type PageHeroProps = { export function PageHero({ title, description }: PageHeroProps) { return ( -
- -
-
-

- {title} -

-

- {title} -

- {description ? ( -

- {description} -

- ) : null} -
-
-
+ +

+ {title} +

+ + {description ? ( +

+ {description} +

+ ) : null} +
); } diff --git a/components/layout/site-hero.tsx b/components/layout/site-hero.tsx new file mode 100644 index 0000000..a94d457 --- /dev/null +++ b/components/layout/site-hero.tsx @@ -0,0 +1,148 @@ +import type { ReactNode } from "react"; + +import { ArrowDown } from "lucide-react"; + +import { Container } from "@/components/layout/container"; +import { HeroMotionBackdrop } from "@/components/layout/hero-motion-backdrop"; +import { cn } from "@/lib/utils"; + +type HeroTone = "default" | "accent" | "soft"; +type HeroLineAlign = "start" | "center" | "end"; + +type HeroTitleLine = { + text: string; + tone?: HeroTone; + align?: HeroLineAlign; +}; + +type HeroShellProps = { + children: ReactNode; + className?: string; + compactBackdrop?: boolean; + containerClassName?: string; + contentClassName?: string; + showBridges?: boolean; + size?: "default" | "narrow" | "wide"; + variant?: "home" | "page"; +}; + +type HeroTitleProps = { + lines: HeroTitleLine[]; + locale?: string; + variant?: "home" | "page"; + className?: string; +}; + +type HeroScrollLinkProps = { + href: string; + label: string; + className?: string; +}; + +const toneClasses: Record = { + default: "", + accent: "hero-title-accent", + soft: "hero-title-accent-soft", +}; + +const lineAlignClasses: Record = { + start: "self-start text-start", + center: "self-center text-center", + end: "self-end text-end", +}; + +export function HeroShell({ + children, + className, + compactBackdrop = false, + containerClassName, + contentClassName, + showBridges = false, + size = "default", + variant = "home", +}: HeroShellProps) { + return ( +
+ + {showBridges ?
: null} + {showBridges ?
: null} + + +
+ {children} +
+
+
+ ); +} + +export function HeroTitle({ + lines, + locale = "en", + variant = "home", + className, +}: HeroTitleProps) { + const isArabic = locale === "ar"; + + return ( +

+ {lines.map((line, index) => ( + + {line.text} + + ))} +

+ ); +} + +export function HeroScrollLink({ + href, + label, + className, +}: HeroScrollLinkProps) { + return ( + + + + ); +}