From 38f891f22df71c8ba31372cbd08634107e2ced7f Mon Sep 17 00:00:00 2001 From: dest Date: Mon, 13 Apr 2026 22:16:45 +0300 Subject: [PATCH] feat(landing): implement footer section with theme toggle and links --- .../[locale]/(landing)/components/footer.tsx | 163 ++++++++++++++++++ .../(landing)/components/hero-section.tsx | 11 +- apps/web/app/[locale]/(landing)/layout.tsx | 2 + .../src/components/common/mode-switch.tsx | 70 ++++++++ .../components/landing/animated-container.tsx | 32 ++++ .../ui/src/components/landing/border-beam.tsx | 107 ++++++++++++ .../components/landing/text-hover-effect.tsx | 134 ++++++++++++++ 7 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 apps/web/app/[locale]/(landing)/components/footer.tsx create mode 100644 packages/core/src/components/common/mode-switch.tsx create mode 100644 packages/ui/src/components/landing/animated-container.tsx create mode 100644 packages/ui/src/components/landing/border-beam.tsx create mode 100644 packages/ui/src/components/landing/text-hover-effect.tsx diff --git a/apps/web/app/[locale]/(landing)/components/footer.tsx b/apps/web/app/[locale]/(landing)/components/footer.tsx new file mode 100644 index 0000000..248dcb1 --- /dev/null +++ b/apps/web/app/[locale]/(landing)/components/footer.tsx @@ -0,0 +1,163 @@ +"use client"; + +import Link from "next/link"; +import type { ReactNode } from "react"; +import { cn } from "@workspace/ui/lib/utils"; +import { Logo } from "@workspace/ui/components/landing/logo"; +import { + GithubIcon, + MessageSquareIcon, + BugIcon, + GitPullRequestIcon, +} from "lucide-react"; +import { ModeSwitch } from "@workspace/core/components/common/mode-switch"; +import { AnimatedContainer } from "@workspace/ui/components/landing/animated-container"; +import { TextHoverEffect } from "@workspace/ui/components/landing/text-hover-effect"; +import { BorderBeam } from "@workspace/ui/components/landing/border-beam"; + +type FooterLink = { + title: string; + href: string; + isExternal: boolean; + icon?: ReactNode; +}; + +type FooterSection = { + label: string; + links: FooterLink[]; +}; + +const footerLinks: FooterSection[] = [ + { + label: "Product", + links: [ + { title: "Pricing", href: "/", isExternal: false }, + { title: "Features", href: "/", isExternal: false }, + { title: "Download", href: "/download", isExternal: false }, + { title: "Showcase", href: "/", isExternal: false }, + ], + }, + { + label: "Resources", + links: [ + { title: "Documentation", href: "/docs", isExternal: false }, + { title: "Quick Start", href: "/docs/quick-start", isExternal: false }, + { + title: "Architecture", + href: "/docs/architecture/overview", + isExternal: false, + }, + { + title: "Changelog", + href: "https://github.com/odest/tntstack/blob/master/CHANGELOG.md", + isExternal: true, + }, + ], + }, + { + label: "Legal", + links: [ + { + title: "MIT License", + href: "https://github.com/odest/tntstack/blob/master/LICENSE", + isExternal: true, + }, + { title: "Privacy Policy", href: "/", isExternal: false }, + { title: "Terms of Service", href: "/", isExternal: false }, + { title: "Security", href: "/", isExternal: false }, + ], + }, + { + label: "Community", + links: [ + { + title: "GitHub", + href: "https://github.com/odest/tntstack", + isExternal: true, + icon: , + }, + { + title: "Contribute", + href: "https://github.com/odest/tntstack/blob/master/CONTRIBUTING.md", + isExternal: true, + icon: , + }, + { + title: "Discussions", + href: "https://github.com/odest/tntstack/discussions", + isExternal: true, + icon: , + }, + { + title: "Report an Issue", + href: "https://github.com/odest/tntstack/issues", + isExternal: true, + icon: , + }, + ], + }, +]; + +export function Footer() { + return ( +
+
+ +
+ + +
+ +

TNTStack

+
+ +

+ Build Cross-Platform Apps Faster Than Ever +

+ +
+ +
+ {footerLinks.map((section, index) => ( + +
+

{section.label}

+
    + {section.links.map((link) => ( +
  • + + {link.icon} + {link.title} + +
  • + ))} +
+
+
+ ))} +
+
+
+ +
+ +
+ ); +} diff --git a/apps/web/app/[locale]/(landing)/components/hero-section.tsx b/apps/web/app/[locale]/(landing)/components/hero-section.tsx index 3aa2e45..16b41dc 100644 --- a/apps/web/app/[locale]/(landing)/components/hero-section.tsx +++ b/apps/web/app/[locale]/(landing)/components/hero-section.tsx @@ -7,6 +7,7 @@ import { Button } from "@workspace/ui/components/button"; import { AnimatedGroup } from "@workspace/ui/components/landing/animated-group"; import { LogoCloud } from "@workspace/ui/components/landing/logo-cloud"; import { TextEffect } from "@workspace/ui/components/landing/text-effect"; +import { BorderBeam } from "@workspace/ui/components/landing/border-beam"; import { transitionVariants } from "@/lib/animations"; export default function HeroSection() { @@ -110,6 +111,7 @@ export default function HeroSection() { className="hidden size-full dark:block" width="3276" height="4095" + priority /> @@ -218,19 +220,24 @@ export default function HeroSection() {
app screen app screen +
diff --git a/apps/web/app/[locale]/(landing)/layout.tsx b/apps/web/app/[locale]/(landing)/layout.tsx index 830aae0..656da8c 100644 --- a/apps/web/app/[locale]/(landing)/layout.tsx +++ b/apps/web/app/[locale]/(landing)/layout.tsx @@ -1,5 +1,6 @@ import { ThemeProvider } from "@workspace/core/providers/theme-provider"; import { HeroHeader } from "./components/header"; +import { Footer } from "./components/footer"; interface LandingLayoutProps { children: React.ReactNode; @@ -16,6 +17,7 @@ export default function LandingLayout({ children }: LandingLayoutProps) { > {children} +