commit 9df1dbfac93f486630c763048d09d1b58a4ab45a
parent e3f72821f194356aab1d900ab3db404eaafda84d
Author: Daniel D’Aquino <daniel@daquino.me>
Date: Wed, 3 Jan 2024 18:22:17 -0800
Optimize purple landing page for different screen sizes
Diffstat:
9 files changed, 104 insertions(+), 82 deletions(-)
diff --git a/content/compiled-locales/en.json b/content/compiled-locales/en.json
@@ -464,7 +464,7 @@
"purple.final_cta.subheadline": [
{
"type": 0,
- "value": "Contribute to the future of the free internet, and look cool doing it."
+ "value": "for as little as U$6.99/month"
}
],
"purple.final_cta.subscribe": [
@@ -473,6 +473,12 @@
"value": "Subscribe"
}
],
+ "purple.final_cta.text": [
+ {
+ "type": 0,
+ "value": "Contribute to the future of the free internet, access exclusive features, and look cool doing it."
+ }
+ ],
"purple.hero.learn-more": [
{
"type": 0,
@@ -551,6 +557,12 @@
"value": "Damus logo"
}
],
+ "topbar.purple": [
+ {
+ "type": 0,
+ "value": "Purple"
+ }
+ ],
"topbar.store": [
{
"type": 0,
diff --git a/content/locales/en.json b/content/locales/en.json
@@ -225,11 +225,14 @@
"string": "Open in Damus"
},
"purple.final_cta.subheadline": {
- "string": "Contribute to the future of the free internet, and look cool doing it."
+ "string": "for as little as U$6.99/month"
},
"purple.final_cta.subscribe": {
"string": "Subscribe"
},
+ "purple.final_cta.text": {
+ "string": "Contribute to the future of the free internet, access exclusive features, and look cool doing it."
+ },
"purple.hero.learn-more": {
"string": "Learn more"
},
@@ -269,6 +272,9 @@
"topbar.logo_alt_text": {
"string": "Damus logo"
},
+ "topbar.purple": {
+ "string": "Purple"
+ },
"topbar.store": {
"string": "Store"
},
diff --git a/src/components/icons/PurpleIcon.tsx b/src/components/icons/PurpleIcon.tsx
@@ -1,9 +1,10 @@
+import { cn } from "@/lib/utils";
import { RoundedContainerWithColorGradientBorder } from "../ui/RoundedContainerWithGradientBorder";
import Image from "next/image";
-export const PurpleIcon = () => {
+export function PurpleIcon({ className }: { className?: string }) {
return (
- <RoundedContainerWithColorGradientBorder className="w-24 h-24 p-[1px]">
+ <RoundedContainerWithColorGradientBorder className={cn("w-24 h-24 p-[1px]", className)}>
<Image src="logo-icon-dark.png" fill className="overflow-hidden w-full h-full object-fill shadow-xl rounded-2xl" alt="Damus Purple logo" />
</RoundedContainerWithColorGradientBorder>
)
diff --git a/src/components/sections/PurpleBenefits.tsx b/src/components/sections/PurpleBenefits.tsx
@@ -11,58 +11,58 @@ import { MeshGradient4 } from "../effects/MeshGradient.4";
import { MeshGradient3 } from "../effects/MeshGradient.3";
export function PurpleBenefits({ className }: { className?: string }) {
- const intl = useIntl()
+ const intl = useIntl()
- const benefits = [
- {
- icon: <Heart className="h-12 w-12 text-white opacity-80"/>,
- headline: intl.formatMessage({ id: "purple.benefits.benefit1.name", defaultMessage: "Help Build The Future" }),
- description: intl.formatMessage({ id: "purple.benefits.benefit1.description", defaultMessage: "Support Damus development to help build the future of decentralized communication on the web." }),
- },
- {
- icon: <Stars className="text-white h-12 w-12 opacity-80"/>,
- headline: intl.formatMessage({ id: "purple.benefits.benefit2.name", defaultMessage: "Exclusive features" }),
- description: intl.formatMessage({ id: "purple.benefits.benefit2.description", defaultMessage: "Be the first to access upcoming premium features: Automatic translations, longer note storage, and more" }),
- },
- {
- icon: <Award className="h-12 w-12 text-white opacity-80"/>,
- headline: intl.formatMessage({ id: "purple.benefits.benefit3.name", defaultMessage: "Supporter Badge" }),
- description: intl.formatMessage({ id: "purple.benefits.benefit3.description", defaultMessage: "Get a special badge on your profile to show everyone your contribution to Freedom tech" }),
- },
- ]
+ const benefits = [
+ {
+ icon: <Heart className="h-12 w-12 text-white opacity-80" />,
+ headline: intl.formatMessage({ id: "purple.benefits.benefit1.name", defaultMessage: "Help Build The Future" }),
+ description: intl.formatMessage({ id: "purple.benefits.benefit1.description", defaultMessage: "Support Damus development to help build the future of decentralized communication on the web." }),
+ },
+ {
+ icon: <Stars className="text-white h-12 w-12 opacity-80" />,
+ headline: intl.formatMessage({ id: "purple.benefits.benefit2.name", defaultMessage: "Exclusive features" }),
+ description: intl.formatMessage({ id: "purple.benefits.benefit2.description", defaultMessage: "Be the first to access upcoming premium features: Automatic translations, longer note storage, and more" }),
+ },
+ {
+ icon: <Award className="h-12 w-12 text-white opacity-80" />,
+ headline: intl.formatMessage({ id: "purple.benefits.benefit3.name", defaultMessage: "Supporter Badge" }),
+ description: intl.formatMessage({ id: "purple.benefits.benefit3.description", defaultMessage: "Get a special badge on your profile to show everyone your contribution to Freedom tech" }),
+ },
+ ]
- return (<>
- <div className={cn("bg-black overflow-hidden relative", className)}>
- <MeshGradient3 className="absolute top-0 left-0 pointer-events-none translate-y-3/4 overflow-visible scale-150"/>
- <div className="container mx-auto px-6 pb-32 pt-20">
- <div className="flex flex-col items-center justify-center mt-32 lg:mt-16">
- <div className="relative mb-32 flex flex-col items-center">
- <motion.h2 className="text-4xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold pb-8 break-keep">
- { intl.formatMessage({ id: "purple.benefits.headline", defaultMessage: "Get more from Damus." }) }
- </motion.h2>
- <motion.div className="text-white/60 text-xl text-center max-w-2xl mb-6 mt-6 break-keep">
- { intl.formatMessage({ id: "purple.benefits.description", defaultMessage: "Help us stay independent in our mission for Freedom tech with our Purple subscription, and look cool doing it!" }) }
- </motion.div>
- </div>
- {(intl.locale != "ja" || process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
- <div className="flex flex-wrap gap-x-8 gap-y-16 items-stretch justify-center">
- {benefits.map((item, index) => (
- <div key={index} className="max-w-xs flex flex-col items-center justify-between">
- <RoundedContainerWithGradientBorder className="">
- {item.icon}
- </RoundedContainerWithGradientBorder>
- <h3 className="text-xl font-semibold text-transparent bg-clip-text bg-gradient-to-br from-white from-30% to-cyan-200 to-100% text-center text-normal mt-6">
- {item.headline}
- </h3>
- <p className="text-white/80 text-center text-normal mt-4">
- {item.description}
- </p>
- </div>
- ))}
- </div>
- )}
+ return (<>
+ <div className={cn("bg-black overflow-hidden relative min-h-screen", className)}>
+ <MeshGradient3 className="absolute top-0 left-0 pointer-events-none translate-y-3/4 overflow-visible scale-150" />
+ <div className="container mx-auto px-6 pt-20">
+ <div className="flex flex-col items-center justify-center lg:mt-16">
+ <div className="relative mb-16 md:mb-32 flex flex-col items-center">
+ <motion.h2 className="text-5xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold pb-8 break-keep">
+ {intl.formatMessage({ id: "purple.benefits.headline", defaultMessage: "Get more from Damus." })}
+ </motion.h2>
+ <motion.div className="text-white/60 text-xl text-center max-w-2xl mb-6 mt-6 break-keep">
+ {intl.formatMessage({ id: "purple.benefits.description", defaultMessage: "Help us stay independent in our mission for Freedom tech with our Purple subscription, and look cool doing it!" })}
+ </motion.div>
+ </div>
+ {(intl.locale != "ja" || process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (
+ <div className="flex flex-wrap gap-x-8 gap-y-16 items-stretch justify-center">
+ {benefits.map((item, index) => (
+ <div key={index} className="max-w-xs flex flex-col items-center justify-between">
+ <RoundedContainerWithGradientBorder className="">
+ {item.icon}
+ </RoundedContainerWithGradientBorder>
+ <h3 className="text-xl font-semibold text-transparent bg-clip-text bg-gradient-to-br from-white from-30% to-cyan-200 to-100% text-center text-normal mt-6">
+ {item.headline}
+ </h3>
+ <p className="text-white/80 text-center text-normal mt-4">
+ {item.description}
+ </p>
</div>
+ ))}
</div>
+ )}
</div>
- </>)
+ </div>
+ </div>
+ </>)
}
diff --git a/src/components/sections/PurpleFAQ.tsx b/src/components/sections/PurpleFAQ.tsx
@@ -53,14 +53,14 @@ export function PurpleFAQ({ className }: { className?: string }) {
<div className={cn("bg-black overflow-hidden relative", className)}>
<MeshGradient3 className="absolute top-0 left-0 pointer-events-none translate-y-3/4 overflow-visible scale-150" />
<div className="container mx-auto px-6 pb-32 pt-20">
- <div className="flex flex-col items-center justify-center mt-32 lg:mt-16">
- <div className="relative mb-32 flex flex-col items-center">
- <motion.h2 className="text-4xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold pb-8 break-keep">
+ <div className="flex flex-col items-center justify-center mt-16">
+ <div className="relative mb-12 flex flex-col items-center">
+ <motion.h2 className="text-5xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold pb-8 break-keep">
{intl.formatMessage({ id: "purple.faq.headline", defaultMessage: "Frequent Questions" })}
</motion.h2>
</div>
</div>
- <Accordion type="single" collapsible className="w-full text-white max-w-3xl mx-auto">
+ <Accordion type="single" collapsible className="w-full text-white max-w-2xl mx-auto">
{faq.map((item, index) => (
<AccordionItem value={`item-${index}`} key={index}>
<AccordionTrigger>{item.question}</AccordionTrigger>
diff --git a/src/components/sections/PurpleFinalCTA.tsx b/src/components/sections/PurpleFinalCTA.tsx
@@ -23,26 +23,29 @@ export function PurpleFinalCTA({ className }: { className?: string }) {
<div className="relative w-24 h-24 mb-8 rounded-xl overflow-hidden shadow-xl">
<PurpleIcon />
</div>
- <motion.h2 className="mb-8 text-4xl md:text-5xl lg:text-6xl text-center text-transparent bg-clip-text bg-gradient-to-r from-white from-5% to-[#E0A4D3] to-100% font-semibold pb-4 whitespace-pre-line leading-relaxed">
+ <motion.h2 className="mb-1 text-4xl md:text-5xl lg:text-6xl text-center text-transparent bg-clip-text bg-gradient-to-r from-white from-5% to-[#E0A4D3] to-100% font-semibold whitespace-pre-line leading-relaxed">
{intl.formatMessage({ id: "purple.final_cta.headline", defaultMessage: "Get Purple today!" })}
</motion.h2>
+ <motion.h3 className="mb-8 text-2xl md:text-3xl lg:text-4xl text-center text-transparent bg-clip-text bg-gradient-to-r from-white from-5% to-[#E0A4D3] to-100% font-semibold pb-4 whitespace-pre-line leading-relaxed">
+ {intl.formatMessage({ id: "purple.final_cta.subheadline", defaultMessage: "for as little as U$6.99/month" })}
+ </motion.h3>
<motion.div className="text-xl text-center max-w-2xl mb-12 text-transparent bg-clip-text bg-gradient-to-br from-white from-5% to-damuspink-500 to-100% whitespace-pre-line leading-loose">
{(intl.locale != "ja" || process.env.FORCE_LOAD_ALL_JA_SECTIONS) && (<>
- {intl.formatMessage({ id: "purple.final_cta.subheadline", defaultMessage: "Contribute to the future of the free internet, and look cool doing it." })}
+ {intl.formatMessage({ id: "purple.final_cta.text", defaultMessage: "Contribute to the future of the free internet, access exclusive features, and look cool doing it." })}
</>)}
</motion.div>
<motion.div
- className="mt-10 md:mt-6 flex flex-col md:flex-row items-center md:items-center gap-y-4 gap-x-6"
+ className="mt-10 md:mt-6 flex flex-col md:flex-row items-center md:items-center justify-center gap-y-4 gap-x-6 w-full"
style={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
>
- <Link href="#">
- <Button variant="default" className="w-full md:w-auto">
+ <Link href="#" className="w-full md:w-auto">
+ <Button variant="default" className="w-full">
{intl.formatMessage({ id: "purple.final_cta.subscribe", defaultMessage: "Subscribe" })}
</Button>
</Link>
- <Link href="#" target="_blank">
- <Button variant="link" className="w-full md:w-auto">
+ <Link href="#" target="_blank" className="w-full md:w-auto">
+ <Button variant="link" className="w-full">
{intl.formatMessage({ id: "purple.final_cta.open_app", defaultMessage: "Open in Damus" })}
<ArrowUpRight className="text-damuspink-600 ml-2" />
</Button>
diff --git a/src/components/sections/PurpleHero.tsx b/src/components/sections/PurpleHero.tsx
@@ -1,4 +1,4 @@
-import { ArrowUpRight, ChevronRight, Globe2 } from "lucide-react";
+import { ArrowUpRight, ChevronRight, Globe2, LucideZapOff, Zap, ZapIcon, ZapOff } from "lucide-react";
import { MeshGradient1 } from "../effects/MeshGradient.1";
import { TopMenu } from "./TopMenu";
import { Button } from "../ui/Button";
@@ -7,7 +7,6 @@ import Link from "next/link";
import { DAMUS_APP_STORE_URL, DAMUS_TESTFLIGHT_URL } from "@/lib/constants";
import { motion } from "framer-motion";
import Image from "next/image";
-import { RoundedContainerWithColorGradientBorder } from "../ui/RoundedContainerWithGradientBorder";
import { PurpleIcon } from "../icons/PurpleIcon";
export function PurpleHero() {
@@ -15,34 +14,33 @@ export function PurpleHero() {
return (<>
<div
- className="bg-black overflow-hidden relative min-h-screen"
+ className="bg-black overflow-hidden relative"
>
<div className="absolute z-0 w-full h-full pointer-events-none">
- <Image src="/stars-bg.webp" fill className="absolute top-0 left-0 object-contain object-center w-full h-full" alt="" aria-hidden="true" />
+ <Image src="/stars-bg.webp" fill className="absolute top-0 left-0 object-cover lg:object-contain object-center w-full h-full" alt="" aria-hidden="true" />
<MeshGradient1 className="-translate-x-1/3" />
</div>
- <div className="container z-10 mx-auto px-6 pb-32 pt-12">
+ <div className="container z-10 mx-auto px-6 pt-12 h-full min-h-screen flex flex-col justify-center">
<TopMenu className="w-full" />
- <div className="relative mb-32 flex flex-col items-center justify-center min-h-screen">
- <div className="flex gap-x-4 items-center mb-6">
- <PurpleIcon />
- <motion.h2 className="text-4xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold break-keep tracking-tight">
+ <div className="flex flex-col items-center justify-center h-full grow">
+ <div className="flex gap-x-4 items-center mb-12 md:mb-6">
+ <PurpleIcon className="w-16 h-16 md:w-24 md:h-24" />
+ <motion.h2 className="text-6xl md:text-8xl text-center text-transparent bg-clip-text bg-gradient-to-r from-damuspink-500 from-30% to-damuspink-600 to-100% font-semibold break-keep tracking-tight">
Purple
</motion.h2>
</div>
<motion.div
- className="mt-10 md:mt-6 flex flex-col md:flex-row items-center md:items-center gap-y-4 gap-x-6"
+ className="mt-10 md:mt-6 flex flex-col md:flex-row items-center md:items-center gap-y-4 gap-x-6 w-full md:w-auto"
style={{ opacity: 0 }}
animate={{ opacity: 1, transition: { delay: 1.5, duration: 1 } }}
>
- <Link href={DAMUS_APP_STORE_URL} target="_blank">
- <Button variant="default" className="w-full md:w-auto">
+ <Link href="#" className="w-full md:w-auto">
+ <Button variant="default" className="w-full">
{intl.formatMessage({ id: "purple.hero.subscribe", defaultMessage: "Subscribe" })}
- <ArrowUpRight className="ml-2" />
</Button>
</Link>
- <Link href={DAMUS_TESTFLIGHT_URL} target="_blank">
- <Button variant="link" className="w-full md:w-auto">
+ <Link href="#" target="_blank" className="w-full md:w-auto">
+ <Button variant="link" className="w-full">
{intl.formatMessage({ id: "purple.hero.learn-more", defaultMessage: "Learn more" })}
<ArrowUpRight className="text-damuspink-600 ml-2" />
</Button>
diff --git a/src/components/sections/TopMenu.tsx b/src/components/sections/TopMenu.tsx
@@ -9,6 +9,7 @@ import { useIntl } from "react-intl";
import { motion } from "framer-motion";
let regularNavItems: { nameIntlId: string, href: string, target?: string }[] = [
+ { nameIntlId: "topbar.purple", href: "/purple" },
{ nameIntlId: "topbar.store", href: DAMUS_MERCH_STORE_URL, target: "_blank" },
{ nameIntlId: "topbar.events", href: "/#events" },
{ nameIntlId: "topbar.team", href: "/#team" },
@@ -23,6 +24,7 @@ export function TopMenu({ className }: { className?: string }) {
// This is needed to allow intl commands to extract the strings
const topbarItemNameIntl: Record<string, string> = {
+ "topbar.purple": intl.formatMessage({ id: "topbar.purple", defaultMessage: "Purple" }),
"topbar.store": intl.formatMessage({ id: "topbar.store", defaultMessage: "Store" }),
"topbar.events": intl.formatMessage({ id: "topbar.events", defaultMessage: "Events" }),
"topbar.team": intl.formatMessage({ id: "topbar.team", defaultMessage: "Our Team" }),
diff --git a/src/components/ui/Accordion.tsx b/src/components/ui/Accordion.tsx
@@ -14,7 +14,7 @@ const AccordionItem = React.forwardRef<
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
- className={cn("border-b", className)}
+ className={cn("border-b border-white/30", className)}
{...props}
/>
))
@@ -28,7 +28,7 @@ const AccordionTrigger = React.forwardRef<
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
- "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
+ "flex flex-1 items-center justify-between py-4 text-sm font-semibold transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}
@@ -46,7 +46,7 @@ const AccordionContent = React.forwardRef<
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
- className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
+ className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down text-white/60"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>