--- template/app/src/landing-page/components/Hero/Orbit.tsx
+++ opensaas-sh/app/src/landing-page/components/Hero/Orbit.tsx
@@ -0,0 +1,282 @@
+import logo from "../../../client/static/logo.webp";
+import nodeLogoDark from "../../../client/static/logos/nodejs-dark.webp";
+import nodeLogo from "../../../client/static/logos/nodejs-light.webp";
+import stripeLogoDark from "../../../client/static/logos/stripe-dark.webp";
+import stripeLogo from "../../../client/static/logos/stripe-light.webp";
+import tailwindLogoDark from "../../../client/static/logos/tailwind-dark.webp";
+import tailwindLogo from "../../../client/static/logos/tailwind-light.webp";
+import { cn } from "../../../client/utils";
+import { AstroLogo } from "../../logos/AstroLogo";
+import { OpenAILogo } from "../../logos/OpenAILogo";
+import { PrismaLogo } from "../../logos/PrismaLogo";
+import { ReactLogo } from "../../logos/ReactLogo";
+import { ShadCNLogo } from "../../logos/ShadCNLogo";
+
+interface LogoConfig {
+ id: string;
+ component: React.ComponentType;
+ circleIndex: number;
+ position: number;
+ size?: number;
+}
+
+function ImageLogo({
+ src,
+ alt,
+ className,
+ dark,
+}: {
+ src: string;
+ alt: string;
+ className?: string;
+ dark?: boolean;
+}) {
+ return (
+
+ );
+}
+
+const logoConfigs: LogoConfig[] = [
+ {
+ id: "wasp",
+ component: () => (
+
+ ),
+ circleIndex: 1,
+ position: 0,
+ },
+ {
+ id: "openai",
+ component: OpenAILogo,
+ circleIndex: 1,
+ position: 120,
+ size: 24,
+ },
+ {
+ id: "astro",
+ component: AstroLogo,
+ circleIndex: 1,
+ position: 270,
+ size: 24,
+ },
+ {
+ id: "prisma",
+ component: PrismaLogo,
+ circleIndex: 2,
+ position: 90,
+ size: 24,
+ },
+ {
+ id: "shadcn",
+ component: ShadCNLogo,
+ circleIndex: 2,
+ position: 210,
+ size: 24,
+ },
+ {
+ id: "tailwind-light",
+ component: () => (
+
+ ),
+ circleIndex: 2,
+ position: 330,
+ },
+ {
+ id: "tailwind-dark",
+ component: () => (
+
+ ),
+ circleIndex: 2,
+ position: 330,
+ },
+ {
+ id: "stripe-light",
+ component: () => (
+
+ ),
+ circleIndex: 3,
+ position: 180,
+ },
+ {
+ id: "stripe-dark",
+ component: () => (
+
+ ),
+ circleIndex: 3,
+ position: 180,
+ },
+ {
+ id: "react",
+ component: ReactLogo,
+ circleIndex: 3,
+ position: 300,
+ size: 32,
+ },
+ {
+ id: "node-light",
+ component: () => (
+
+ ),
+ circleIndex: 3,
+ position: 50,
+ },
+ {
+ id: "node-dark",
+ component: () => (
+
+ ),
+ circleIndex: 3,
+ position: 50,
+ },
+];
+
+const circles = [
+ // Innermost to outermost
+ { radius: 120, orbitPeriod: 72, ringPeriod: 180 },
+ { radius: 180, orbitPeriod: 120, ringPeriod: 240 },
+ { radius: 240, orbitPeriod: 180, ringPeriod: 360 },
+ { radius: 300, orbitPeriod: 360, ringPeriod: 720 },
+];
+
+const ringGradients = [
+ `conic-gradient(from 0deg,
+ hsl(var(--primary) / 0.15),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.15),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.05),
+ hsl(var(--primary) / 0.15)
+ )`,
+ `conic-gradient(from 45deg,
+ hsl(var(--primary) / 0.12),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.08),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.2),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.12)
+ )`,
+ `conic-gradient(from 90deg,
+ hsl(var(--primary) / 0.1),
+ hsl(var(--primary) / 0.18),
+ hsl(var(--primary) / 0.1),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.15),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.1)
+ )`,
+ `conic-gradient(from 135deg,
+ hsl(var(--primary) / 0.08),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.16),
+ hsl(var(--primary) / 0.06),
+ hsl(var(--primary) / 0),
+ hsl(var(--primary) / 0.12),
+ hsl(var(--primary) / 0.08)
+ )`,
+];
+
+export function Orbit() {
+ return (
+
+
+
+
+
+ 100%
+
+
Open Source
+
+
+ {circles.map((circle, circleIndex) => (
+
+ ))}
+
+ {logoConfigs.map((logoConfig) => {
+ const circle = circles[logoConfig.circleIndex];
+ const logoSize = logoConfig.size || 32;
+ const LogoComponent = logoConfig.component;
+
+ return (
+
+ );
+ })}
+
+ );
+}