chore: format
This commit is contained in:
@@ -3,274 +3,274 @@
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 221.2 83.2% 53.3%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 221.2 83.2% 53.3%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 221.2 83.2% 53.3%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 221.2 83.2% 53.3%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 217.2 91.2% 59.8%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 224.3 76.5% 48%;
|
||||
}
|
||||
.dark {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 217.2 91.2% 59.8%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 224.3 76.5% 48%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.animation-delay-500 {
|
||||
animation-delay: 500ms;
|
||||
}
|
||||
.animation-delay-500 {
|
||||
animation-delay: 500ms;
|
||||
}
|
||||
|
||||
.animation-delay-700 {
|
||||
animation-delay: 700ms;
|
||||
}
|
||||
.animation-delay-700 {
|
||||
animation-delay: 700ms;
|
||||
}
|
||||
|
||||
/* Space-themed animations */
|
||||
.perspective {
|
||||
perspective: 1000px;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
/* Space-themed animations */
|
||||
.perspective {
|
||||
perspective: 1000px;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.envelope-flap {
|
||||
transform-origin: top;
|
||||
transform: rotateX(20deg);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.envelope-flap {
|
||||
transform-origin: top;
|
||||
transform: rotateX(20deg);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.letter {
|
||||
transform-origin: center;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.letter {
|
||||
transform-origin: center;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes float-space {
|
||||
0% {
|
||||
transform: translateY(0px) rotate(6deg) translateZ(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px) rotate(6deg) translateZ(10px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px) rotate(6deg) translateZ(0px);
|
||||
}
|
||||
}
|
||||
@keyframes float-space {
|
||||
0% {
|
||||
transform: translateY(0px) rotate(6deg) translateZ(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-10px) rotate(6deg) translateZ(10px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px) rotate(6deg) translateZ(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-float-space {
|
||||
animation: float-space 6s ease-in-out infinite;
|
||||
}
|
||||
.animate-float-space {
|
||||
animation: float-space 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes orbit {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes orbit {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-orbit {
|
||||
animation: orbit 20s linear infinite;
|
||||
}
|
||||
.animate-orbit {
|
||||
animation: orbit 20s linear infinite;
|
||||
}
|
||||
|
||||
.animate-orbit-reverse {
|
||||
animation: orbit 15s linear infinite reverse;
|
||||
}
|
||||
.animate-orbit-reverse {
|
||||
animation: orbit 15s linear infinite reverse;
|
||||
}
|
||||
|
||||
.animate-orbit-slow {
|
||||
animation: orbit 30s linear infinite;
|
||||
}
|
||||
.animate-orbit-slow {
|
||||
animation: orbit 30s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes twinkle {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
@keyframes twinkle {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-twinkle {
|
||||
animation: twinkle 2s ease-in-out infinite;
|
||||
}
|
||||
.animate-twinkle {
|
||||
animation: twinkle 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Stars */
|
||||
.star {
|
||||
@apply absolute rounded-full bg-white;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
/* Stars */
|
||||
.star {
|
||||
@apply absolute rounded-full bg-white;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.star-1 {
|
||||
top: 10%;
|
||||
left: 20%;
|
||||
animation: twinkle 3s infinite 0.1s;
|
||||
}
|
||||
.star-1 {
|
||||
top: 10%;
|
||||
left: 20%;
|
||||
animation: twinkle 3s infinite 0.1s;
|
||||
}
|
||||
|
||||
.star-2 {
|
||||
top: 30%;
|
||||
left: 70%;
|
||||
animation: twinkle 2s infinite 0.3s;
|
||||
}
|
||||
.star-2 {
|
||||
top: 30%;
|
||||
left: 70%;
|
||||
animation: twinkle 2s infinite 0.3s;
|
||||
}
|
||||
|
||||
.star-3 {
|
||||
top: 60%;
|
||||
left: 30%;
|
||||
animation: twinkle 4s infinite 0.5s;
|
||||
}
|
||||
.star-3 {
|
||||
top: 60%;
|
||||
left: 30%;
|
||||
animation: twinkle 4s infinite 0.5s;
|
||||
}
|
||||
|
||||
.star-4 {
|
||||
top: 80%;
|
||||
left: 80%;
|
||||
animation: twinkle 3s infinite 0.7s;
|
||||
}
|
||||
.star-4 {
|
||||
top: 80%;
|
||||
left: 80%;
|
||||
animation: twinkle 3s infinite 0.7s;
|
||||
}
|
||||
|
||||
.star-5 {
|
||||
top: 15%;
|
||||
left: 50%;
|
||||
animation: twinkle 5s infinite 0.2s;
|
||||
}
|
||||
.star-5 {
|
||||
top: 15%;
|
||||
left: 50%;
|
||||
animation: twinkle 5s infinite 0.2s;
|
||||
}
|
||||
|
||||
.star-6 {
|
||||
top: 45%;
|
||||
left: 15%;
|
||||
animation: twinkle 2.5s infinite 0.4s;
|
||||
}
|
||||
.star-6 {
|
||||
top: 45%;
|
||||
left: 15%;
|
||||
animation: twinkle 2.5s infinite 0.4s;
|
||||
}
|
||||
|
||||
.star-7 {
|
||||
top: 75%;
|
||||
left: 45%;
|
||||
animation: twinkle 3.5s infinite 0.6s;
|
||||
}
|
||||
.star-7 {
|
||||
top: 75%;
|
||||
left: 45%;
|
||||
animation: twinkle 3.5s infinite 0.6s;
|
||||
}
|
||||
|
||||
.star-8 {
|
||||
top: 25%;
|
||||
left: 85%;
|
||||
animation: twinkle 4.5s infinite 0.8s;
|
||||
}
|
||||
.star-8 {
|
||||
top: 25%;
|
||||
left: 85%;
|
||||
animation: twinkle 4.5s infinite 0.8s;
|
||||
}
|
||||
|
||||
.star-9 {
|
||||
top: 90%;
|
||||
left: 10%;
|
||||
animation: twinkle 3s infinite 0.9s;
|
||||
}
|
||||
.star-9 {
|
||||
top: 90%;
|
||||
left: 10%;
|
||||
animation: twinkle 3s infinite 0.9s;
|
||||
}
|
||||
|
||||
.star-10 {
|
||||
top: 5%;
|
||||
left: 35%;
|
||||
animation: twinkle 2s infinite 1s;
|
||||
}
|
||||
.star-10 {
|
||||
top: 5%;
|
||||
left: 35%;
|
||||
animation: twinkle 2s infinite 1s;
|
||||
}
|
||||
|
||||
.star-11 {
|
||||
top: 50%;
|
||||
left: 95%;
|
||||
animation: twinkle 4s infinite 1.1s;
|
||||
}
|
||||
.star-11 {
|
||||
top: 50%;
|
||||
left: 95%;
|
||||
animation: twinkle 4s infinite 1.1s;
|
||||
}
|
||||
|
||||
.star-12 {
|
||||
top: 35%;
|
||||
left: 5%;
|
||||
animation: twinkle 3s infinite 1.2s;
|
||||
}
|
||||
.star-12 {
|
||||
top: 35%;
|
||||
left: 5%;
|
||||
animation: twinkle 3s infinite 1.2s;
|
||||
}
|
||||
|
||||
/* Particles */
|
||||
.particle {
|
||||
@apply absolute rounded-full;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
/* Particles */
|
||||
.particle {
|
||||
@apply absolute rounded-full;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.particle-1 {
|
||||
@apply bg-[#4ECDC4];
|
||||
top: 30%;
|
||||
left: 40%;
|
||||
animation: particle-float 8s infinite linear;
|
||||
}
|
||||
.particle-1 {
|
||||
@apply bg-[#4ECDC4];
|
||||
top: 30%;
|
||||
left: 40%;
|
||||
animation: particle-float 8s infinite linear;
|
||||
}
|
||||
|
||||
.particle-2 {
|
||||
@apply bg-yellow-400;
|
||||
top: 60%;
|
||||
left: 60%;
|
||||
animation: particle-float 12s infinite 2s linear;
|
||||
}
|
||||
.particle-2 {
|
||||
@apply bg-yellow-400;
|
||||
top: 60%;
|
||||
left: 60%;
|
||||
animation: particle-float 12s infinite 2s linear;
|
||||
}
|
||||
|
||||
.particle-3 {
|
||||
@apply bg-white;
|
||||
top: 40%;
|
||||
left: 70%;
|
||||
animation: particle-float 10s infinite 1s linear;
|
||||
}
|
||||
.particle-3 {
|
||||
@apply bg-white;
|
||||
top: 40%;
|
||||
left: 70%;
|
||||
animation: particle-float 10s infinite 1s linear;
|
||||
}
|
||||
|
||||
.particle-4 {
|
||||
@apply bg-[#4ECDC4];
|
||||
top: 70%;
|
||||
left: 30%;
|
||||
animation: particle-float 9s infinite 3s linear;
|
||||
}
|
||||
.particle-4 {
|
||||
@apply bg-[#4ECDC4];
|
||||
top: 70%;
|
||||
left: 30%;
|
||||
animation: particle-float 9s infinite 3s linear;
|
||||
}
|
||||
|
||||
.particle-5 {
|
||||
@apply bg-yellow-400;
|
||||
top: 20%;
|
||||
left: 50%;
|
||||
animation: particle-float 11s infinite 4s linear;
|
||||
}
|
||||
.particle-5 {
|
||||
@apply bg-yellow-400;
|
||||
top: 20%;
|
||||
left: 50%;
|
||||
animation: particle-float 11s infinite 4s linear;
|
||||
}
|
||||
|
||||
@keyframes particle-float {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translate(calc(random(100) * 1px), calc(random(100) * 1px));
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes particle-float {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translate(calc(random(100) * 1px), calc(random(100) * 1px));
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +1,86 @@
|
||||
import type { Metadata, Viewport } from "next"
|
||||
import { Geist, Geist_Mono } from "next/font/google"
|
||||
import "./globals.css"
|
||||
import { Analytics } from "@/components/analytics"
|
||||
import type { Metadata, Viewport } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { Analytics } from "@/components/analytics";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
})
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
})
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "LetterSpace | Self-hosted Newsletter Platform",
|
||||
description:
|
||||
"Open source newsletter platform for managing your subscribers and sending beautiful emails, all self-hosted for complete control and privacy.",
|
||||
keywords: [
|
||||
"newsletter",
|
||||
"email marketing",
|
||||
"self-hosted",
|
||||
"open source",
|
||||
"subscriber management",
|
||||
],
|
||||
authors: [{ name: "LetterSpace Team" }],
|
||||
creator: "LetterSpace",
|
||||
publisher: "LetterSpace",
|
||||
metadataBase: new URL("https://letterspace.app"),
|
||||
alternates: {
|
||||
canonical: "/",
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
openGraph: {
|
||||
type: "website",
|
||||
title: "LetterSpace | Self-hosted Newsletter Platform",
|
||||
description:
|
||||
"Open source newsletter platform for managing your subscribers and sending beautiful emails, all self-hosted for complete control and privacy.",
|
||||
siteName: "LetterSpace",
|
||||
images: [
|
||||
{
|
||||
url: "/cover.png",
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: "LetterSpace - Self-hosted Newsletter Platform",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "LetterSpace | Self-hosted Newsletter Platform",
|
||||
description:
|
||||
"Open source newsletter platform for managing your subscribers and sending beautiful emails, all self-hosted for complete control and privacy.",
|
||||
images: ["/cover.png"],
|
||||
creator: "@letterspace",
|
||||
},
|
||||
applicationName: "LetterSpace",
|
||||
category: "Email Marketing",
|
||||
}
|
||||
title: "LetterSpace | Self-hosted Newsletter Platform",
|
||||
description:
|
||||
"Open source newsletter platform for managing your subscribers and sending beautiful emails, all self-hosted for complete control and privacy.",
|
||||
keywords: [
|
||||
"newsletter",
|
||||
"email marketing",
|
||||
"self-hosted",
|
||||
"open source",
|
||||
"subscriber management",
|
||||
],
|
||||
authors: [{ name: "LetterSpace Team" }],
|
||||
creator: "LetterSpace",
|
||||
publisher: "LetterSpace",
|
||||
metadataBase: new URL("https://letterspace.app"),
|
||||
alternates: {
|
||||
canonical: "/",
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
openGraph: {
|
||||
type: "website",
|
||||
title: "LetterSpace | Self-hosted Newsletter Platform",
|
||||
description:
|
||||
"Open source newsletter platform for managing your subscribers and sending beautiful emails, all self-hosted for complete control and privacy.",
|
||||
siteName: "LetterSpace",
|
||||
images: [
|
||||
{
|
||||
url: "/cover.png",
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: "LetterSpace - Self-hosted Newsletter Platform",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "LetterSpace | Self-hosted Newsletter Platform",
|
||||
description:
|
||||
"Open source newsletter platform for managing your subscribers and sending beautiful emails, all self-hosted for complete control and privacy.",
|
||||
images: ["/cover.png"],
|
||||
creator: "@letterspace",
|
||||
},
|
||||
applicationName: "LetterSpace",
|
||||
category: "Email Marketing",
|
||||
};
|
||||
|
||||
export const viewport: Viewport = {
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
maximumScale: 1,
|
||||
}
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
maximumScale: 1,
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<Analytics />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<Analytics />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Header } from "@/components/header"
|
||||
import { Hero } from "@/components/hero"
|
||||
import { Features, featuresData } from "@/components/features"
|
||||
import { Footer } from "@/components/footer"
|
||||
import { Header } from "@/components/header";
|
||||
import { Hero } from "@/components/hero";
|
||||
import { Features, featuresData } from "@/components/features";
|
||||
import { Footer } from "@/components/footer";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="min-h-screen bg-[#121212] text-white">
|
||||
<Header />
|
||||
<Hero />
|
||||
<Features features={featuresData} />
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className="min-h-screen bg-[#121212] text-white">
|
||||
<Header />
|
||||
<Hero />
|
||||
<Features features={featuresData} />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import Script from "next/script"
|
||||
import Script from "next/script";
|
||||
|
||||
export function Analytics() {
|
||||
return (
|
||||
<Script
|
||||
id="plausible-script"
|
||||
strategy="afterInteractive"
|
||||
defer
|
||||
data-domain="letterspace.app"
|
||||
src="https://analytics.letterspace.app/js/script.js"
|
||||
/>
|
||||
)
|
||||
return (
|
||||
<Script
|
||||
id="plausible-script"
|
||||
strategy="afterInteractive"
|
||||
defer
|
||||
data-domain="letterspace.app"
|
||||
src="https://analytics.letterspace.app/js/script.js"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,115 +1,115 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import Link from "next/link"
|
||||
import type { LucideIcon } from "lucide-react"
|
||||
import Link from "next/link";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import {
|
||||
Zap,
|
||||
Wifi,
|
||||
Database,
|
||||
MessageSquare,
|
||||
FileCode,
|
||||
Settings,
|
||||
BugPlay,
|
||||
ChevronDown,
|
||||
} from "lucide-react"
|
||||
import constants from "@/constants"
|
||||
Zap,
|
||||
Wifi,
|
||||
Database,
|
||||
MessageSquare,
|
||||
FileCode,
|
||||
Settings,
|
||||
BugPlay,
|
||||
ChevronDown,
|
||||
} from "lucide-react";
|
||||
import constants from "@/constants";
|
||||
|
||||
interface Feature {
|
||||
icon: React.ReactElement<LucideIcon>
|
||||
title: string
|
||||
description: string
|
||||
link?: {
|
||||
text: string
|
||||
url: string
|
||||
}
|
||||
icon: React.ReactElement<LucideIcon>;
|
||||
title: string;
|
||||
description: string;
|
||||
link?: {
|
||||
text: string;
|
||||
url: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const featuresData: Feature[] = [
|
||||
{
|
||||
icon: <Zap className="h-6 w-6 text-yellow-400" />,
|
||||
title: "Zero-Config",
|
||||
description: "Sensible built-in default configs for common use cases",
|
||||
},
|
||||
// {
|
||||
// icon: <Code className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
// title: "Extensible",
|
||||
// description:
|
||||
// "Expose the full ability to customize the behavior of the platform",
|
||||
// },
|
||||
{
|
||||
icon: <Wifi className="h-6 w-6 text-gray-300" />,
|
||||
title: "SMTP Support",
|
||||
description: "Connect any SMTP server to send emails to your subscribers",
|
||||
},
|
||||
{
|
||||
icon: <Database className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
title: "Unlimited Lists",
|
||||
description: "Create and manage as many newsletter lists as you need",
|
||||
},
|
||||
{
|
||||
icon: <MessageSquare className="h-6 w-6 text-gray-300" />,
|
||||
title: "Campaign Management",
|
||||
description: "Built-in support for creating and scheduling email campaigns",
|
||||
},
|
||||
// {
|
||||
// icon: <RefreshCw className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
// title: "Auto-scaling",
|
||||
// description: "Automatically handle thousands of subscribers with ease",
|
||||
// },
|
||||
{
|
||||
icon: <FileCode className="h-6 w-6 text-yellow-400" />,
|
||||
title: "REST API",
|
||||
description: "Comprehensive API for integrating with your applications",
|
||||
link: {
|
||||
text: "API Documentation",
|
||||
url: constants.env.DOCS_URL + "/api",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: <Settings className="h-6 w-6 text-gray-300" />,
|
||||
title: "Advanced Analytics",
|
||||
description: "Track opens, clicks, and engagement for all your campaigns",
|
||||
},
|
||||
{
|
||||
icon: <BugPlay className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
title: "Built by Developers",
|
||||
description:
|
||||
"For developers, by developers. Everything you need to build and test with ease.",
|
||||
link: {
|
||||
text: "Development Guide",
|
||||
url: constants.env.DOCS_URL,
|
||||
},
|
||||
},
|
||||
]
|
||||
{
|
||||
icon: <Zap className="h-6 w-6 text-yellow-400" />,
|
||||
title: "Zero-Config",
|
||||
description: "Sensible built-in default configs for common use cases",
|
||||
},
|
||||
// {
|
||||
// icon: <Code className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
// title: "Extensible",
|
||||
// description:
|
||||
// "Expose the full ability to customize the behavior of the platform",
|
||||
// },
|
||||
{
|
||||
icon: <Wifi className="h-6 w-6 text-gray-300" />,
|
||||
title: "SMTP Support",
|
||||
description: "Connect any SMTP server to send emails to your subscribers",
|
||||
},
|
||||
{
|
||||
icon: <Database className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
title: "Unlimited Lists",
|
||||
description: "Create and manage as many newsletter lists as you need",
|
||||
},
|
||||
{
|
||||
icon: <MessageSquare className="h-6 w-6 text-gray-300" />,
|
||||
title: "Campaign Management",
|
||||
description: "Built-in support for creating and scheduling email campaigns",
|
||||
},
|
||||
// {
|
||||
// icon: <RefreshCw className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
// title: "Auto-scaling",
|
||||
// description: "Automatically handle thousands of subscribers with ease",
|
||||
// },
|
||||
{
|
||||
icon: <FileCode className="h-6 w-6 text-yellow-400" />,
|
||||
title: "REST API",
|
||||
description: "Comprehensive API for integrating with your applications",
|
||||
link: {
|
||||
text: "API Documentation",
|
||||
url: constants.env.DOCS_URL + "/api",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: <Settings className="h-6 w-6 text-gray-300" />,
|
||||
title: "Advanced Analytics",
|
||||
description: "Track opens, clicks, and engagement for all your campaigns",
|
||||
},
|
||||
{
|
||||
icon: <BugPlay className="h-6 w-6 text-[#4ECDC4]" />,
|
||||
title: "Built by Developers",
|
||||
description:
|
||||
"For developers, by developers. Everything you need to build and test with ease.",
|
||||
link: {
|
||||
text: "Development Guide",
|
||||
url: constants.env.DOCS_URL,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const Features = ({ features }: { features: Feature[] }) => (
|
||||
<section className="container mx-auto px-4 py-16">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{features.map((feature, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="relative group overflow-hidden bg-[#1a1a1a] p-6 rounded-lg border border-gray-800 transition-all duration-300 ease-in-out hover:shadow-lg hover:shadow-[#4ECDC4]/10
|
||||
<section className="container mx-auto px-4 py-16">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{features.map((feature, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="relative group overflow-hidden bg-[#1a1a1a] p-6 rounded-lg border border-gray-800 transition-all duration-300 ease-in-out hover:shadow-lg hover:shadow-[#4ECDC4]/10
|
||||
before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite]
|
||||
before:bg-gradient-to-r before:from-transparent before:via-white/10 before:to-transparent"
|
||||
>
|
||||
<div className="relative z-10">
|
||||
<div className="bg-[#252525] w-12 h-12 rounded-lg flex items-center justify-center mb-4">
|
||||
{feature.icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
|
||||
<p className="text-gray-400">{feature.description}</p>
|
||||
{feature.link && (
|
||||
<Link
|
||||
href={feature.link.url}
|
||||
className="text-[#4ECDC4] hover:underline flex items-center gap-1 mt-4 text-sm"
|
||||
>
|
||||
{feature.link.text}{" "}
|
||||
<ChevronDown className="h-4 w-4 rotate-270" />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
>
|
||||
<div className="relative z-10">
|
||||
<div className="bg-[#252525] w-12 h-12 rounded-lg flex items-center justify-center mb-4">
|
||||
{feature.icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
|
||||
<p className="text-gray-400">{feature.description}</p>
|
||||
{feature.link && (
|
||||
<Link
|
||||
href={feature.link.url}
|
||||
className="text-[#4ECDC4] hover:underline flex items-center gap-1 mt-4 text-sm"
|
||||
>
|
||||
{feature.link.text}{" "}
|
||||
<ChevronDown className="h-4 w-4 rotate-270" />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import Link from "next/link"
|
||||
import { constants } from "@/constants"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link";
|
||||
import { constants } from "@/constants";
|
||||
import Image from "next/image";
|
||||
|
||||
export const Footer = () => (
|
||||
<footer className="border-t border-gray-800 py-8 mt-12">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex flex-col items-center justify-center gap-6">
|
||||
<div className="flex items-center">
|
||||
<span className="text-white font-bold">Letter</span>
|
||||
<span className="text-primary font-bold">Space</span>
|
||||
<span className="text-gray-400 text-sm ml-4">
|
||||
©{new Date().getFullYear()} LetterSpace
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-6">
|
||||
<Link
|
||||
href={constants.env.DOCS_URL}
|
||||
className="text-gray-400 hover:text-white"
|
||||
>
|
||||
Documentation
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.GITHUB_URL}
|
||||
className="text-gray-400 hover:text-white"
|
||||
target="_blank"
|
||||
>
|
||||
GitHub
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.DOCS_URL + "/api"}
|
||||
className="text-gray-400 hover:text-white"
|
||||
>
|
||||
API
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.X_URL}
|
||||
className="text-gray-400 hover:text-white flex items-center gap-1.5"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
src="/x.svg"
|
||||
alt="X"
|
||||
width={20}
|
||||
height={20}
|
||||
style={{ filter: "invert(1)" }}
|
||||
/>
|
||||
Built by dcodes
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
<footer className="border-t border-gray-800 py-8 mt-12">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex flex-col items-center justify-center gap-6">
|
||||
<div className="flex items-center">
|
||||
<span className="text-white font-bold">Letter</span>
|
||||
<span className="text-primary font-bold">Space</span>
|
||||
<span className="text-gray-400 text-sm ml-4">
|
||||
©{new Date().getFullYear()} LetterSpace
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-6">
|
||||
<Link
|
||||
href={constants.env.DOCS_URL}
|
||||
className="text-gray-400 hover:text-white"
|
||||
>
|
||||
Documentation
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.GITHUB_URL}
|
||||
className="text-gray-400 hover:text-white"
|
||||
target="_blank"
|
||||
>
|
||||
GitHub
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.DOCS_URL + "/api"}
|
||||
className="text-gray-400 hover:text-white"
|
||||
>
|
||||
API
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.X_URL}
|
||||
className="text-gray-400 hover:text-white flex items-center gap-1.5"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
src="/x.svg"
|
||||
alt="X"
|
||||
width={20}
|
||||
height={20}
|
||||
style={{ filter: "invert(1)" }}
|
||||
/>
|
||||
Built by dcodes
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import Link from "next/link"
|
||||
import { Menu } from "lucide-react"
|
||||
import { Button } from "@repo/ui"
|
||||
import { constants } from "@/constants"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link";
|
||||
import { Menu } from "lucide-react";
|
||||
import { Button } from "@repo/ui";
|
||||
import { constants } from "@/constants";
|
||||
import Image from "next/image";
|
||||
|
||||
export const Header = () => (
|
||||
<header className="border-b border-gray-800">
|
||||
<div className="container mx-auto px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/" className="flex items-center font-bold text-xl">
|
||||
<span className="text-white">Letter</span>
|
||||
<span className="text-primary">Space</span>
|
||||
</Link>
|
||||
</div>
|
||||
<header className="border-b border-gray-800">
|
||||
<div className="container mx-auto px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/" className="flex items-center font-bold text-xl">
|
||||
<span className="text-white">Letter</span>
|
||||
<span className="text-primary">Space</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<nav className="hidden md:flex items-center gap-6">
|
||||
<Link
|
||||
href="#"
|
||||
className="text-gray-300 hover:text-white flex items-center gap-1"
|
||||
>
|
||||
v{constants.version}
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.GITHUB_URL}
|
||||
className="text-gray-300 hover:text-white"
|
||||
target="_blank"
|
||||
>
|
||||
<Image
|
||||
src="/github.svg"
|
||||
alt="GitHub"
|
||||
width={30}
|
||||
height={30}
|
||||
style={{ filter: "invert(1)" }}
|
||||
/>
|
||||
</Link>
|
||||
</nav>
|
||||
<nav className="hidden md:flex items-center gap-6">
|
||||
<Link
|
||||
href="#"
|
||||
className="text-gray-300 hover:text-white flex items-center gap-1"
|
||||
>
|
||||
v{constants.version}
|
||||
</Link>
|
||||
<Link
|
||||
href={constants.env.GITHUB_URL}
|
||||
className="text-gray-300 hover:text-white"
|
||||
target="_blank"
|
||||
>
|
||||
<Image
|
||||
src="/github.svg"
|
||||
alt="GitHub"
|
||||
width={30}
|
||||
height={30}
|
||||
style={{ filter: "invert(1)" }}
|
||||
/>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<Button variant="ghost" size="icon" className="md:hidden">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Menu className="h-6 w-6" />
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
<Button variant="ghost" size="icon" className="md:hidden">
|
||||
<span className="sr-only">Open menu</span>
|
||||
<Menu className="h-6 w-6" />
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
@@ -1,118 +1,118 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import constants from "@/constants"
|
||||
import { Button } from "@repo/ui"
|
||||
import { Mail, Star, Zap } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import constants from "@/constants";
|
||||
import { Button } from "@repo/ui";
|
||||
import { Mail, Star, Zap } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export const Hero = () => (
|
||||
<section className="container mx-auto px-4 py-16 md:py-24 flex flex-col md:flex-row items-center justify-between">
|
||||
<div className="md:w-1/2 space-y-4">
|
||||
<h2 className="text-3xl font-bold">
|
||||
Letter<span className="text-primary">Space</span>
|
||||
</h2>
|
||||
<h1 className="text-5xl md:text-6xl font-bold text-white">
|
||||
Open Source Email Platform
|
||||
</h1>
|
||||
<div className="space-y-2 max-w-xl">
|
||||
<p className="text-gray-400 text-xl">
|
||||
Connect any SMTP server and manage unlimited newsletters
|
||||
</p>
|
||||
<p className="text-gray-400 text-xl">
|
||||
Zero-config and developer-friendly
|
||||
</p>
|
||||
<p className="text-gray-400 text-xl">LetterSpace for everyone</p>
|
||||
</div>
|
||||
<section className="container mx-auto px-4 py-16 md:py-24 flex flex-col md:flex-row items-center justify-between">
|
||||
<div className="md:w-1/2 space-y-4">
|
||||
<h2 className="text-3xl font-bold">
|
||||
Letter<span className="text-primary">Space</span>
|
||||
</h2>
|
||||
<h1 className="text-5xl md:text-6xl font-bold text-white">
|
||||
Open Source Email Platform
|
||||
</h1>
|
||||
<div className="space-y-2 max-w-xl">
|
||||
<p className="text-gray-400 text-xl">
|
||||
Connect any SMTP server and manage unlimited newsletters
|
||||
</p>
|
||||
<p className="text-gray-400 text-xl">
|
||||
Zero-config and developer-friendly
|
||||
</p>
|
||||
<p className="text-gray-400 text-xl">LetterSpace for everyone</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 pt-4">
|
||||
<Link href={constants.env.DOCS_URL}>
|
||||
<Button className="bg-[#4ECDC4] hover:bg-[#3DBDB5] text-black font-medium px-6">
|
||||
Get Started
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={constants.env.GITHUB_URL}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-gray-700 text-gray-300 hover:text-white hover:bg-gray-800 bg-gray-900"
|
||||
>
|
||||
View on GitHub
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4 pt-4">
|
||||
<Link href={constants.env.DOCS_URL}>
|
||||
<Button className="bg-[#4ECDC4] hover:bg-[#3DBDB5] text-black font-medium px-6">
|
||||
Get Started
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={constants.env.GITHUB_URL}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="border-gray-700 text-gray-300 hover:text-white hover:bg-gray-800 bg-gray-900"
|
||||
>
|
||||
View on GitHub
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="md:w-1/2 flex justify-center mt-12 md:mt-0">
|
||||
<div className="relative w-80 h-80">
|
||||
{/* Space background with stars */}
|
||||
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-[#1a1a1a] to-[#121212] overflow-hidden">
|
||||
{/* Stars */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="star star-1"></div>
|
||||
<div className="star star-2"></div>
|
||||
<div className="star star-3"></div>
|
||||
<div className="star star-4"></div>
|
||||
<div className="star star-5"></div>
|
||||
<div className="star star-6"></div>
|
||||
<div className="star star-7"></div>
|
||||
<div className="star star-8"></div>
|
||||
<div className="star star-9"></div>
|
||||
<div className="star star-10"></div>
|
||||
<div className="star star-11"></div>
|
||||
<div className="star star-12"></div>
|
||||
</div>
|
||||
<div className="md:w-1/2 flex justify-center mt-12 md:mt-0">
|
||||
<div className="relative w-80 h-80">
|
||||
{/* Space background with stars */}
|
||||
<div className="absolute inset-0 rounded-full bg-gradient-to-br from-[#1a1a1a] to-[#121212] overflow-hidden">
|
||||
{/* Stars */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="star star-1"></div>
|
||||
<div className="star star-2"></div>
|
||||
<div className="star star-3"></div>
|
||||
<div className="star star-4"></div>
|
||||
<div className="star star-5"></div>
|
||||
<div className="star star-6"></div>
|
||||
<div className="star star-7"></div>
|
||||
<div className="star star-8"></div>
|
||||
<div className="star star-9"></div>
|
||||
<div className="star star-10"></div>
|
||||
<div className="star star-11"></div>
|
||||
<div className="star star-12"></div>
|
||||
</div>
|
||||
|
||||
{/* Animated nebula/galaxy effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#4ECDC4]/10 to-transparent blur-2xl animate-pulse"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-tr from-yellow-400/5 to-transparent blur-3xl animate-pulse animation-delay-700"></div>
|
||||
</div>
|
||||
{/* Animated nebula/galaxy effect */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[#4ECDC4]/10 to-transparent blur-2xl animate-pulse"></div>
|
||||
<div className="absolute inset-0 bg-gradient-to-tr from-yellow-400/5 to-transparent blur-3xl animate-pulse animation-delay-700"></div>
|
||||
</div>
|
||||
|
||||
{/* Floating envelope in space */}
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
{/* Envelope with 3D perspective */}
|
||||
<div className="relative w-48 h-36 bg-gradient-to-br from-[#4ECDC4] to-[#2A9D94] rounded-lg shadow-lg transform rotate-6 animate-float-space perspective">
|
||||
{/* Envelope flap */}
|
||||
<div className="absolute top-0 left-0 w-full h-1/3 bg-gradient-to-b from-[#5DDED6] to-[#4ECDC4] rounded-t-lg envelope-flap"></div>
|
||||
{/* Floating envelope in space */}
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
{/* Envelope with 3D perspective */}
|
||||
<div className="relative w-48 h-36 bg-gradient-to-br from-[#4ECDC4] to-[#2A9D94] rounded-lg shadow-lg transform rotate-6 animate-float-space perspective">
|
||||
{/* Envelope flap */}
|
||||
<div className="absolute top-0 left-0 w-full h-1/3 bg-gradient-to-b from-[#5DDED6] to-[#4ECDC4] rounded-t-lg envelope-flap"></div>
|
||||
|
||||
{/* Envelope body */}
|
||||
<div className="absolute bottom-0 left-0 w-full h-2/3 bg-[#4ECDC4] rounded-b-lg border-t border-white/20"></div>
|
||||
{/* Envelope body */}
|
||||
<div className="absolute bottom-0 left-0 w-full h-2/3 bg-[#4ECDC4] rounded-b-lg border-t border-white/20"></div>
|
||||
|
||||
{/* Letter peeking out */}
|
||||
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-5/6 h-3/4 bg-white rounded-sm shadow-inner transform -translate-y-2 letter">
|
||||
<div className="w-full h-full p-2 flex flex-col justify-center">
|
||||
<div className="w-full h-2 bg-gray-300 rounded-full mb-2"></div>
|
||||
<div className="w-3/4 h-2 bg-gray-300 rounded-full mb-2"></div>
|
||||
<div className="w-5/6 h-2 bg-gray-300 rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Letter peeking out */}
|
||||
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-5/6 h-3/4 bg-white rounded-sm shadow-inner transform -translate-y-2 letter">
|
||||
<div className="w-full h-full p-2 flex flex-col justify-center">
|
||||
<div className="w-full h-2 bg-gray-300 rounded-full mb-2"></div>
|
||||
<div className="w-3/4 h-2 bg-gray-300 rounded-full mb-2"></div>
|
||||
<div className="w-5/6 h-2 bg-gray-300 rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Orbiting elements */}
|
||||
<div className="absolute w-full h-full animate-orbit">
|
||||
<div className="absolute -right-4 -top-8">
|
||||
<Star className="h-6 w-6 text-yellow-400 animate-twinkle" />
|
||||
</div>
|
||||
</div>
|
||||
{/* Orbiting elements */}
|
||||
<div className="absolute w-full h-full animate-orbit">
|
||||
<div className="absolute -right-4 -top-8">
|
||||
<Star className="h-6 w-6 text-yellow-400 animate-twinkle" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute w-full h-full animate-orbit-reverse">
|
||||
<div className="absolute -left-8 top-0">
|
||||
<Mail className="h-8 w-8 text-[#4ECDC4] animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute w-full h-full animate-orbit-reverse">
|
||||
<div className="absolute -left-8 top-0">
|
||||
<Mail className="h-8 w-8 text-[#4ECDC4] animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute w-full h-full animate-orbit-slow">
|
||||
<div className="absolute right-0 bottom-0">
|
||||
<Zap className="h-10 w-10 text-yellow-400 filter drop-shadow-lg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute w-full h-full animate-orbit-slow">
|
||||
<div className="absolute right-0 bottom-0">
|
||||
<Zap className="h-10 w-10 text-yellow-400 filter drop-shadow-lg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating particles */}
|
||||
<div className="particle particle-1"></div>
|
||||
<div className="particle particle-2"></div>
|
||||
<div className="particle particle-3"></div>
|
||||
<div className="particle particle-4"></div>
|
||||
<div className="particle particle-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
{/* Floating particles */}
|
||||
<div className="particle particle-1"></div>
|
||||
<div className="particle particle-2"></div>
|
||||
<div className="particle particle-3"></div>
|
||||
<div className="particle particle-4"></div>
|
||||
<div className="particle particle-5"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { APP_VERSION } from "@repo/shared"
|
||||
import { APP_VERSION } from "@repo/shared";
|
||||
|
||||
export const constants = {
|
||||
env: {
|
||||
DOCS_URL: process.env.NEXT_PUBLIC_DOCS_URL!,
|
||||
GITHUB_URL: process.env.NEXT_PUBLIC_GITHUB_URL!,
|
||||
X_URL: "https://x.com/dcodesdev",
|
||||
},
|
||||
version: APP_VERSION,
|
||||
}
|
||||
env: {
|
||||
DOCS_URL: process.env.NEXT_PUBLIC_DOCS_URL!,
|
||||
GITHUB_URL: process.env.NEXT_PUBLIC_GITHUB_URL!,
|
||||
X_URL: "https://x.com/dcodesdev",
|
||||
},
|
||||
version: APP_VERSION,
|
||||
};
|
||||
|
||||
Object.entries(constants.env).forEach(([key, value]) => {
|
||||
if (!value) {
|
||||
throw new Error(`${key} is not defined`)
|
||||
}
|
||||
})
|
||||
if (!value) {
|
||||
throw new Error(`${key} is not defined`);
|
||||
}
|
||||
});
|
||||
|
||||
export default constants
|
||||
export default constants;
|
||||
|
||||
Reference in New Issue
Block a user