Skip to content

Commit

Permalink
perf(ui): updated hero section
Browse files Browse the repository at this point in the history
  • Loading branch information
FindMalek committed Oct 8, 2024
1 parent 3d84600 commit fb2cf0e
Show file tree
Hide file tree
Showing 14 changed files with 14,230 additions and 11,045 deletions.
4 changes: 2 additions & 2 deletions components/app/auth-user-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { authUserEmailSchema as userAuthSchema } from "@/config/validation"
import { cn } from "@/lib/utils"
import { toast } from "@/hooks/use-toast"

import { GithubSignIn } from "@/components/shared/github-sign-in"
import { GoogleSignIn } from "@/components/shared/google-sign-in"
import { Icons } from "@/components/shared/icons"
import { buttonVariants } from "@/components/ui/button"
import {
Expand All @@ -24,8 +26,6 @@ import {
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

import { GithubSignIn } from "@/components/shared/github-sign-in"
import { GoogleSignIn } from "@/components/shared/google-sign-in"
import { isBanned, isRegisteredUser } from "@/actions/user"

export function AuthUserEmail() {
Expand Down
75 changes: 50 additions & 25 deletions components/app/marketing-hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import { news } from "@/config/site"
import { cn } from "@/lib/utils"

import { MarketingHeroInstall } from "@/components/app/marketing-hero-install"
import { MarketingVideo } from "@/components/app/marketing-video"
import { AnimatedBadge } from "@/components/fancy/animated-badge"
import { buttonVariants } from "@/components/ui/button"
import { FramerComponentWrapper } from "@/components/layout/framer-wrapper"
import { Icons } from "@/components/shared/icons"
import { Button, buttonVariants } from "@/components/ui/button"

export function MarketingHero() {
return (
Expand Down Expand Up @@ -76,37 +79,56 @@ export function MarketingHero() {
<div className="mt-10 grid grid-cols-1 md:mt-20">
<div className="flex flex-col items-center gap-6 px-4 pb-8 text-center md:px-10">
<AnimatedBadge href={news.url} title={news.overview} />
<div className="relative flex flex-col items-center gap-4 lg:flex-row">
<h1

<div className="relative flex flex-col items-center gap-4">
<FramerComponentWrapper
className={cn(
"relative mx-0 max-w-7xl pt-5 md:mx-auto md:px-4 md:py-2",
"relative mx-0 max-w-full pt-5 md:mx-auto md:px-4 md:py-2",
"text-balance font-bold tracking-tighter",
"text-5xl sm:text-7xl md:text-7xl lg:text-7xl"
)}
initial={{ filter: "blur(10px)", opacity: 0, y: 50 }}
animate={{ filter: "blur(0px)", opacity: 1, y: 0 }}
transition={{
duration: 1,
staggerChildren: 0.2,
}}
>
Build Your High Performance, Easy, and Fast AI-Powered Project
</h1>
</FramerComponentWrapper>
<FramerComponentWrapper
className="max-w-6xl text-balance text-center text-base tracking-tight text-muted-foreground md:text-lg"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
delay: 0.6,
duration: 0.8,
}}
>
Scale your applications efficiently with our easy-to-use AI
inference. Our Large Language Models (LLMs) are{" "}
<b>lightning-fast</b> and consume{" "}
<b>significantly less energy</b>, providing you with unbeatable
performance and sustainability.
</FramerComponentWrapper>
</div>

<p className="max-w-6xl text-balance text-center text-base tracking-tight text-muted-foreground md:text-lg">
Scale your applications efficiently with our easy-to-use AI
inference. Our Large Language Models (LLMs) are{" "}
<b>lightning-fast</b> and consume <b>significantly less energy</b>
, providing you with unbeatable performance and sustainability.
</p>

<div className="mx-auto flex w-full max-w-full flex-col items-center justify-center gap-4 py-1 sm:max-w-xl sm:flex-row md:mx-auto">
<Link
href="/#cta"
className={cn(
buttonVariants({
variant: "default",
size: "lg",
}),
"w-full sm:w-auto"
)}
>
Join our waitlist
<FramerComponentWrapper
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.8, duration: 0.8 }}
className="mx-auto flex w-full max-w-full flex-col items-center justify-center gap-4 py-1 sm:max-w-xl sm:flex-row md:mx-auto"
>
<Link href="/#cta">
<Button
variant="expandIcon"
Icon={Icons.chevronRight}
iconPlacement="right"
size="lg"
className="w-full sm:w-auto"
>
Join our waitlist
</Button>
</Link>
<Link
href="/docs"
Expand All @@ -120,11 +142,14 @@ export function MarketingHero() {
>
Learn Undrstnd
</Link>
</div>
</FramerComponentWrapper>
<MarketingHeroInstall />
</div>
</div>
</div>

<MarketingVideo />
<div className="pointer-events-none absolute inset-x-0 -bottom-12 h-1/3 bg-gradient-to-t from-background via-background to-transparent lg:h-1/4" />
</section>
)
}
23 changes: 23 additions & 0 deletions components/app/marketing-video.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react"

import { HeroVideoDialog } from "@/components/fancy/hero-video-dialog"
import { FramerComponentWrapper } from "@/components/layout/framer-wrapper"

export function MarketingVideo() {
return (
<FramerComponentWrapper
className="relative mx-auto flex w-full items-center justify-center pb-8"
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 1.2, duration: 1 }}
>
<HeroVideoDialog
animationStyle="from-center"
videoSrc="https://www.youtube.com/watch?v=KQrSe4ZFpUU"
thumbnailSrc="/marketing/video-preview.png"
thumbnailAlt="Hero Video"
className="max-w-screen-lg rounded-lg border shadow-lg"
/>
</FramerComponentWrapper>
)
}
141 changes: 141 additions & 0 deletions components/fancy/hero-video-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"use client"

import { useState } from "react"
import Image from "next/image"
import { AnimatePresence, motion } from "framer-motion"
import { Play, XIcon } from "lucide-react"

import { cn } from "@/lib/utils"

type AnimationStyle =
| "from-bottom"
| "from-center"
| "from-top"
| "from-left"
| "from-right"
| "fade"
| "top-in-bottom-out"
| "left-in-right-out"

interface HeroVideoProps {
animationStyle?: AnimationStyle
videoSrc: string
thumbnailSrc: string
thumbnailAlt?: string
className?: string
}

const animationVariants = {
"from-bottom": {
initial: { y: "100%", opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: "100%", opacity: 0 },
},
"from-center": {
initial: { scale: 0.5, opacity: 0 },
animate: { scale: 1, opacity: 1 },
exit: { scale: 0.5, opacity: 0 },
},
"from-top": {
initial: { y: "-100%", opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: "-100%", opacity: 0 },
},
"from-left": {
initial: { x: "-100%", opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: "-100%", opacity: 0 },
},
"from-right": {
initial: { x: "100%", opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: "100%", opacity: 0 },
},
fade: {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
},
"top-in-bottom-out": {
initial: { y: "-100%", opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: "100%", opacity: 0 },
},
"left-in-right-out": {
initial: { x: "-100%", opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: "100%", opacity: 0 },
},
}

export function HeroVideoDialog({
animationStyle = "from-center",
videoSrc,
thumbnailSrc,
thumbnailAlt = "Video thumbnail",
className,
}: HeroVideoProps) {
const [isVideoOpen, setIsVideoOpen] = useState(false)
const selectedAnimation = animationVariants[animationStyle]

return (
<div className={cn("relative", className)}>
<div
className="group relative cursor-pointer rounded-md p-2 ring-1 ring-slate-200/50 backdrop-blur-md dark:bg-gray-900/70 dark:ring-white/10"
onClick={() => setIsVideoOpen(true)}
>
<Image
src={thumbnailSrc}
alt={thumbnailAlt}
width={1920}
height={1080}
className="rounded-md border transition-all duration-200 ease-out group-hover:brightness-[0.8]"
/>
<div className="absolute inset-0 flex scale-[0.9] items-center justify-center rounded-2xl transition-all duration-200 ease-out group-hover:scale-100">
<div className="z-30 flex size-28 items-center justify-center rounded-full bg-primary/10 backdrop-blur-md">
<div
className={`relative flex size-20 scale-100 items-center justify-center rounded-full bg-gradient-to-b from-primary/30 to-primary shadow-md transition-all duration-200 ease-out group-hover:scale-[1.2]`}
>
<Play
className="size-8 scale-100 fill-white text-white transition-transform duration-200 ease-out group-hover:scale-105"
style={{
filter:
"drop-shadow(0 4px 3px rgb(0 0 0 / 0.07)) drop-shadow(0 2px 2px rgb(0 0 0 / 0.06))",
}}
/>
</div>
</div>
</div>
</div>
<AnimatePresence>
{isVideoOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
onClick={() => setIsVideoOpen(false)}
exit={{ opacity: 0 }}
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-md"
>
<motion.div
{...selectedAnimation}
transition={{ type: "spring", damping: 30, stiffness: 300 }}
className="relative mx-4 aspect-video w-full max-w-4xl md:mx-0"
>
<motion.button className="absolute -top-16 right-0 rounded-full bg-neutral-900/50 p-2 text-xl text-white ring-1 backdrop-blur-md dark:bg-neutral-100/50 dark:text-black">
<XIcon className="size-5" />
</motion.button>
<div className="relative isolate z-[1] size-full overflow-hidden rounded-2xl border-2 border-white">
<iframe
src={videoSrc}
className="size-full rounded-2xl"
allowFullScreen
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
></iframe>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
)
}
34 changes: 34 additions & 0 deletions components/fancy/word-pull-up.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client"

import React from "react"
import { motion } from "framer-motion"

const ease = [0.16, 1, 0.3, 1]

export function WordPullUp({
text,
className,
}: {
text: string[]
className?: string
}) {
return (
<div className={className}>
{text.map((text, index) => (
<motion.span
key={index}
className="px-1 md:px-2"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.8,
delay: index * 0.2,
ease,
}}
>
{text}
</motion.span>
))}
</div>
)
}
33 changes: 33 additions & 0 deletions components/layout/framer-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { motion } from "framer-motion"

const ease = [0.16, 1, 0.3, 1]

export function FramerWrapper(props: { children: React.ReactNode }) {
return (
<motion.div
Expand All @@ -15,3 +17,34 @@ export function FramerWrapper(props: { children: React.ReactNode }) {
</motion.div>
)
}

export function FramerComponentWrapper({
children,
initial,
animate,
exit,
className,
transition,
}: {
children: React.ReactNode
initial: { y: number; opacity: number; filter?: string }
animate: { y: number; opacity: number; filter?: string }
exit?: { y: number; opacity: number; filter?: string }
transition: { duration: number; delay?: number; staggerChildren?: number }
className?: string
}) {
return (
<motion.div
className={className}
initial={initial}
animate={animate}
exit={exit}
transition={{
...transition,
ease,
}}
>
{children}
</motion.div>
)
}
13 changes: 11 additions & 2 deletions components/shared/github-sign-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@ import { AuthDetailsType } from "@/types/auth"
import { Icons } from "@/components/shared/icons"
import { Button } from "@/components/ui/button"

export function GithubSignIn({ data, type }: { data?: AuthDetailsType, type: "login" | "register" }) {
export function GithubSignIn({
data,
type,
}: {
data?: AuthDetailsType
type: "login" | "register"
}) {
const [isLoading, setLoading] = useState<boolean>(false)

const handleSignIn = async () => {
setLoading(true)
signIn("github", {
redirect: false,
callbackUrl: type === "login" ? "/dashboard" : `/onboarding?name=${data?.name}&type=${data?.type}`,
callbackUrl:
type === "login"
? "/dashboard"
: `/onboarding?name=${data?.name}&type=${data?.type}`,
})
setLoading(false)
}
Expand Down
Loading

0 comments on commit fb2cf0e

Please sign in to comment.