damus.io

damus.io website
git clone git://jb55.com/damus.io
Log | Files | Refs | README | LICENSE

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:
Mcontent/compiled-locales/en.json | 14+++++++++++++-
Mcontent/locales/en.json | 8+++++++-
Msrc/components/icons/PurpleIcon.tsx | 5+++--
Msrc/components/sections/PurpleBenefits.tsx | 98++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/components/sections/PurpleFAQ.tsx | 8++++----
Msrc/components/sections/PurpleFinalCTA.tsx | 17++++++++++-------
Msrc/components/sections/PurpleHero.tsx | 28+++++++++++++---------------
Msrc/components/sections/TopMenu.tsx | 2++
Msrc/components/ui/Accordion.tsx | 6+++---
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>