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
|
||||
className={`flex items-center rounded-full border border-white/10 py-1 px-1.5 text-sm relative transition-all duration-500 ease-out ${
|
||||
scrolled
|
||||
? "bg-white/5 backdrop-blur-md w-[470px]"
|
||||
: "bg-transparent w-[335px]"
|
||||
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 ? "w-[470px]" : "w-[335px]"
|
||||
}`}
|
||||
>
|
||||
<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 { HomeLayout } from "fumadocs-ui/layouts/home";
|
||||
import type { Metadata } from "next";
|
||||
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 }) {
|
||||
return <HomeLayout {...baseOptions}>{children}</HomeLayout>;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use client";
|
||||
import ShinyText from "components/ShinyText/ShinyText";
|
||||
import { Poppins } from "next/font/google";
|
||||
import React from "react";
|
||||
@@ -6,6 +7,8 @@ import CodeContainer from "./_components/CodeContainer";
|
||||
import NpmPackage from "./_components/NpmPackage";
|
||||
import SideCircles from "./_components/SideCircles";
|
||||
import Spotlight from "./_components/Spotlight";
|
||||
import TechConstellation from "./_components/TechConstellation";
|
||||
import TerminalDisplay from "./_components/Terminal";
|
||||
|
||||
const poppins = Poppins({
|
||||
subsets: ["latin"],
|
||||
@@ -21,7 +24,7 @@ export default function HomePage() {
|
||||
<BackgroundGradients />
|
||||
<Spotlight />
|
||||
<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="flex flex-col items-center justify-center space-y-4 text-center">
|
||||
<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>
|
||||
</div>
|
||||
<TerminalDisplay />
|
||||
<TechConstellation />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,3 +36,19 @@
|
||||
.shiny-text.disabled {
|
||||
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 { RootProvider } from "fumadocs-ui/provider";
|
||||
import { Inter } from "next/font/google";
|
||||
import { Poppins } from "next/font/google";
|
||||
import { type ReactNode, Suspense } from "react";
|
||||
import Navbar from "./(home)/_components/Navbar";
|
||||
|
||||
const inter = Inter({
|
||||
const poppins = Poppins({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600", "700", "800"],
|
||||
});
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
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">
|
||||
<RootProvider
|
||||
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