mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
added terminal design and tech stack
This commit is contained in:
@@ -55,10 +55,8 @@ const Navbar = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`flex items-center rounded-full border border-white/10 py-1 px-1.5 text-sm relative transition-all duration-500 ease-out ${
|
className={`flex items-center backdrop-blur-md bg-white/5 rounded-full border border-white/10 py-1 px-1.5 text-sm relative transition-all duration-500 ease-out ${
|
||||||
scrolled
|
scrolled ? "w-[470px]" : "w-[335px]"
|
||||||
? "bg-white/5 backdrop-blur-md w-[470px]"
|
|
||||||
: "bg-transparent w-[335px]"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
163
apps/web/src/app/(home)/_components/TechConstellation.tsx
Normal file
163
apps/web/src/app/(home)/_components/TechConstellation.tsx
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { technologies } from "@/lib/constant";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
type TechConstellationProp = {
|
||||||
|
fromRef: React.RefObject<HTMLElement>;
|
||||||
|
toRef: React.RefObject<HTMLElement>;
|
||||||
|
containerRef: React.RefObject<HTMLElement>;
|
||||||
|
delay?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AnimatedBeam = ({
|
||||||
|
fromRef,
|
||||||
|
toRef,
|
||||||
|
containerRef,
|
||||||
|
delay = 0,
|
||||||
|
}: TechConstellationProp) => {
|
||||||
|
const [path, setPath] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updatePath = () => {
|
||||||
|
if (!fromRef.current || !toRef.current || !containerRef.current) return;
|
||||||
|
|
||||||
|
const containerRect = containerRef.current.getBoundingClientRect();
|
||||||
|
const fromRect = fromRef.current.getBoundingClientRect();
|
||||||
|
const toRect = toRef.current.getBoundingClientRect();
|
||||||
|
|
||||||
|
const fromX = fromRect.left - containerRect.left + fromRect.width / 2;
|
||||||
|
const fromY = fromRect.top - containerRect.top + fromRect.height / 2;
|
||||||
|
const toX = toRect.left - containerRect.left + toRect.width / 2;
|
||||||
|
const toY = toRect.top - containerRect.top + toRect.height / 2;
|
||||||
|
|
||||||
|
setPath(
|
||||||
|
`M ${fromX},${fromY} Q ${(fromX + toX) / 2},${(fromY + toY) / 2 - 50} ${toX},${toY}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
updatePath();
|
||||||
|
window.addEventListener("resize", updatePath);
|
||||||
|
return () => window.removeEventListener("resize", updatePath);
|
||||||
|
}, [fromRef, toRef, containerRef]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg className="absolute top-0 left-0 w-full h-full pointer-events-none">
|
||||||
|
<title>Tech Stack</title>
|
||||||
|
<path
|
||||||
|
d={path}
|
||||||
|
fill="none"
|
||||||
|
stroke="url(#gradient)"
|
||||||
|
strokeWidth="2"
|
||||||
|
className="opacity-50"
|
||||||
|
>
|
||||||
|
<animate
|
||||||
|
attributeName="stroke-dasharray"
|
||||||
|
values="0,1000;1000,0"
|
||||||
|
dur="3s"
|
||||||
|
begin={`${delay}s`}
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</path>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
|
<stop offset="0%" stopColor="#3B82F6" stopOpacity="0" />
|
||||||
|
<stop offset="50%" stopColor="#3B82F6" />
|
||||||
|
<stop offset="100%" stopColor="#3B82F6" stopOpacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TechConstellation = () => {
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const centerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const techRefs = useRef<{ [key: string]: HTMLDivElement | null }>({});
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsVisible(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className="relative w-full h-screen bg-gradient-to-b from-transparent to-gray-950 overflow-hidden flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={centerRef}
|
||||||
|
className={`absolute z-10 w-32 h-32 bg-blue-600 rounded-xl flex items-center justify-center transform transition-all duration-1000 ${isVisible ? "scale-100 opacity-100" : "scale-0 opacity-0"}`}
|
||||||
|
>
|
||||||
|
<span className="text-4xl font-bold text-white">TS</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{technologies.map((tech, index) => {
|
||||||
|
const radius = 250;
|
||||||
|
const x = Math.cos((tech.angle * Math.PI) / 180) * radius;
|
||||||
|
const y = Math.sin((tech.angle * Math.PI) / 180) * radius;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={tech.name}
|
||||||
|
ref={(el) => {
|
||||||
|
techRefs.current[tech.name] = el;
|
||||||
|
}}
|
||||||
|
className={`absolute z-20 transform -translate-x-1/2 -translate-y-1/2 transition-all duration-1000
|
||||||
|
${isVisible ? "scale-100 opacity-100" : "scale-0 opacity-0"}`}
|
||||||
|
style={{
|
||||||
|
left: `calc(50% + ${x}px)`,
|
||||||
|
top: `calc(50% + ${y}px)`,
|
||||||
|
transitionDelay: `${index * 100}ms`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`w-16 h-16 ${tech.color} rounded-full flex items-center justify-center
|
||||||
|
transform hover:scale-125 transition-all duration-300 cursor-pointer
|
||||||
|
shadow-lg hover:shadow-xl hover:rotate-12`}
|
||||||
|
>
|
||||||
|
<tech.icon className={`w-8 h-8 ${tech.textColor}`} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="opacity-0 group-hover:opacity-100 absolute -top-12 left-1/2 transform -translate-x-1/2
|
||||||
|
bg-gray-900 text-white px-4 py-2 rounded-lg shadow-xl transition-all duration-300
|
||||||
|
whitespace-nowrap text-sm hover:scale-105"
|
||||||
|
>
|
||||||
|
<strong>{tech.name}</strong>
|
||||||
|
<p className="text-gray-300 text-xs">{tech.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{isVisible &&
|
||||||
|
technologies.map((tech, index) => (
|
||||||
|
<AnimatedBeam
|
||||||
|
key={`beam-${tech.name}`}
|
||||||
|
fromRef={centerRef as React.RefObject<HTMLElement>}
|
||||||
|
toRef={{ current: techRefs.current[tech.name] as HTMLElement }}
|
||||||
|
containerRef={containerRef as React.RefObject<HTMLElement>}
|
||||||
|
delay={index * 0.2}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
|
{[...Array(20)].map((_, i) => (
|
||||||
|
<div
|
||||||
|
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
||||||
|
key={`star-${i}`}
|
||||||
|
className="absolute w-2 h-2 bg-blue-500 rounded-full opacity-20"
|
||||||
|
style={{
|
||||||
|
left: `${Math.random() * 100}%`,
|
||||||
|
top: `${Math.random() * 100}%`,
|
||||||
|
animationDelay: `${Math.random() * 5}s`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TechConstellation;
|
||||||
56
apps/web/src/app/(home)/_components/Terminal.tsx
Normal file
56
apps/web/src/app/(home)/_components/Terminal.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const TerminalDisplay = () => {
|
||||||
|
const TITLE_TEXT = `
|
||||||
|
╔════════════════════════════════════════════════════════════╗
|
||||||
|
║ ║
|
||||||
|
║ ██████╗ ███████╗████████╗████████╗███████╗██████╗ ║
|
||||||
|
║ ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗ ║
|
||||||
|
║ ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝ ║
|
||||||
|
║ ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗ ║
|
||||||
|
║ ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║ ║
|
||||||
|
║ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ║
|
||||||
|
║ ║
|
||||||
|
║ ████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗ ║
|
||||||
|
║ ╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ ║
|
||||||
|
║ ██║ ███████╗ ██║ ███████║██║ █████╔╝ ║
|
||||||
|
║ ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗ ║
|
||||||
|
║ ██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗ ║
|
||||||
|
║ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ║
|
||||||
|
║ ║
|
||||||
|
║ The Modern Full-Stack Framework ║
|
||||||
|
║ ║
|
||||||
|
╚════════════════════════════════════════════════════════════╝
|
||||||
|
`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl mx-auto p-6 mt-12">
|
||||||
|
<div className="bg-gray-900/30 backdrop-blur-3xl rounded-lg shadow-xl overflow-hidden">
|
||||||
|
<div className="bg-gray-800/30 backdrop-blur-3xl px-4 py-2 flex items-center">
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<div className="w-3 h-3 rounded-full bg-red-500" />
|
||||||
|
<div className="w-3 h-3 rounded-full bg-yellow-500" />
|
||||||
|
<div className="w-3 h-3 rounded-full bg-green-500" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-4 font-mono text-sm flex flex-col">
|
||||||
|
<div className="flex items-center text-gray-300 mb-4">
|
||||||
|
<span className="text-green-400">➜</span>
|
||||||
|
<span className="text-blue-400 ml-2">~</span>
|
||||||
|
<span className="ml-2">$</span>
|
||||||
|
<span className="ml-2 text-white">
|
||||||
|
npx create-better-t-stack@latest
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre className="text-blue-400 whitespace-pre overflow-x-auto px-8">
|
||||||
|
{TITLE_TEXT}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TerminalDisplay;
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
import { baseOptions } from "@/app/layout.config";
|
import { baseOptions } from "@/app/layout.config";
|
||||||
import { HomeLayout } from "fumadocs-ui/layouts/home";
|
import { HomeLayout } from "fumadocs-ui/layouts/home";
|
||||||
|
import type { Metadata } from "next";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Better-T-Stack",
|
||||||
|
description: "Unleash the power of better-t-stack",
|
||||||
|
};
|
||||||
|
|
||||||
export default function Layout({ children }: { children: ReactNode }) {
|
export default function Layout({ children }: { children: ReactNode }) {
|
||||||
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
|
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
"use client";
|
||||||
import ShinyText from "components/ShinyText/ShinyText";
|
import ShinyText from "components/ShinyText/ShinyText";
|
||||||
import { Poppins } from "next/font/google";
|
import { Poppins } from "next/font/google";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -6,6 +7,8 @@ import CodeContainer from "./_components/CodeContainer";
|
|||||||
import NpmPackage from "./_components/NpmPackage";
|
import NpmPackage from "./_components/NpmPackage";
|
||||||
import SideCircles from "./_components/SideCircles";
|
import SideCircles from "./_components/SideCircles";
|
||||||
import Spotlight from "./_components/Spotlight";
|
import Spotlight from "./_components/Spotlight";
|
||||||
|
import TechConstellation from "./_components/TechConstellation";
|
||||||
|
import TerminalDisplay from "./_components/Terminal";
|
||||||
|
|
||||||
const poppins = Poppins({
|
const poppins = Poppins({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
@@ -21,7 +24,7 @@ export default function HomePage() {
|
|||||||
<BackgroundGradients />
|
<BackgroundGradients />
|
||||||
<Spotlight />
|
<Spotlight />
|
||||||
<SideCircles />
|
<SideCircles />
|
||||||
<div className="max-w-6xl mx-auto text-center mb-16 relative z-50">
|
<div className="max-w-6xl mx-auto text-center mb-16 relative z-50 ">
|
||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
<div className="flex flex-col items-center justify-center space-y-4 text-center">
|
<div className="flex flex-col items-center justify-center space-y-4 text-center">
|
||||||
<NpmPackage />
|
<NpmPackage />
|
||||||
@@ -57,6 +60,8 @@ export default function HomePage() {
|
|||||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-500/20 to-pink-500/20 blur-3xl transform -skew-y-12" />
|
<div className="absolute inset-0 bg-gradient-to-r from-purple-500/20 to-pink-500/20 blur-3xl transform -skew-y-12" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<TerminalDisplay />
|
||||||
|
<TechConstellation />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,3 +36,19 @@
|
|||||||
.shiny-text.disabled {
|
.shiny-text.disabled {
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fadeIn {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeInUp 0.5s ease-out forwards;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import "./global.css";
|
import "./global.css";
|
||||||
import { RootProvider } from "fumadocs-ui/provider";
|
import { RootProvider } from "fumadocs-ui/provider";
|
||||||
import { Inter } from "next/font/google";
|
import { Poppins } from "next/font/google";
|
||||||
import { type ReactNode, Suspense } from "react";
|
import { type ReactNode, Suspense } from "react";
|
||||||
import Navbar from "./(home)/_components/Navbar";
|
import Navbar from "./(home)/_components/Navbar";
|
||||||
|
|
||||||
const inter = Inter({
|
const poppins = Poppins({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
weight: ["400", "500", "600", "700", "800"],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function Layout({ children }: { children: ReactNode }) {
|
export default function Layout({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" className={inter.className} suppressHydrationWarning>
|
<html lang="en" className={poppins.className} suppressHydrationWarning>
|
||||||
<body className="flex flex-col min-h-screen relative bg-black">
|
<body className="flex flex-col min-h-screen relative bg-black">
|
||||||
<RootProvider
|
<RootProvider
|
||||||
search={{
|
search={{
|
||||||
|
|||||||
105
apps/web/src/lib/constant.ts
Normal file
105
apps/web/src/lib/constant.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import {
|
||||||
|
AppWindow,
|
||||||
|
Binary,
|
||||||
|
Boxes,
|
||||||
|
Component,
|
||||||
|
Database,
|
||||||
|
FastForward,
|
||||||
|
Lock,
|
||||||
|
Palette,
|
||||||
|
ServerCog,
|
||||||
|
Workflow,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
export const technologies = [
|
||||||
|
{
|
||||||
|
name: "Bun",
|
||||||
|
category: "core",
|
||||||
|
angle: -60,
|
||||||
|
icon: FastForward,
|
||||||
|
color: "bg-yellow-100",
|
||||||
|
textColor: "text-black",
|
||||||
|
description: "Fast all-in-one JavaScript runtime",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TypeScript",
|
||||||
|
category: "core",
|
||||||
|
angle: -30,
|
||||||
|
icon: Binary,
|
||||||
|
color: "bg-blue-500",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "Type safety across the stack",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tRPC",
|
||||||
|
category: "core",
|
||||||
|
angle: 0,
|
||||||
|
icon: Workflow,
|
||||||
|
color: "bg-blue-600",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "End-to-end type-safe APIs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TanStack Router",
|
||||||
|
category: "frontend",
|
||||||
|
angle: 60,
|
||||||
|
icon: AppWindow,
|
||||||
|
color: "bg-red-500",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "Type-safe routing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Tailwind CSS",
|
||||||
|
category: "frontend",
|
||||||
|
angle: 90,
|
||||||
|
icon: Palette,
|
||||||
|
color: "bg-sky-400",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "Utility-first CSS framework",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shadcn/ui",
|
||||||
|
category: "frontend",
|
||||||
|
angle: 120,
|
||||||
|
icon: Component,
|
||||||
|
color: "bg-gray-900",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "Re-usable components",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hono",
|
||||||
|
category: "backend",
|
||||||
|
angle: 180,
|
||||||
|
icon: ServerCog,
|
||||||
|
color: "bg-orange-500",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "Ultrafast web framework",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Better-Auth",
|
||||||
|
category: "backend",
|
||||||
|
angle: 210,
|
||||||
|
icon: Lock,
|
||||||
|
color: "bg-indigo-600",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "Modern authentication solution",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Drizzle ORM",
|
||||||
|
category: "backend",
|
||||||
|
angle: 240,
|
||||||
|
icon: Database,
|
||||||
|
color: "bg-green-400",
|
||||||
|
textColor: "text-black",
|
||||||
|
description: "TypeScript ORM",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "libSQL",
|
||||||
|
category: "backend",
|
||||||
|
angle: 270,
|
||||||
|
icon: Boxes,
|
||||||
|
color: "bg-gray-600",
|
||||||
|
textColor: "text-white",
|
||||||
|
description: "SQLite-compatible database engine",
|
||||||
|
},
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user