diff --git a/apps/web/package.json b/apps/web/package.json index 32a1491..aae6617 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,11 +17,12 @@ "fumadocs-core": "15.1.2", "fumadocs-mdx": "11.5.7", "fumadocs-ui": "15.1.2", - "lucide-react": "^0.483.0", + "lucide-react": "^0.485.0", "motion": "^12.5.0", "next": "15.2.3", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-tweet": "^3.2.2" }, "devDependencies": { "@tailwindcss/postcss": "^4.0.15", diff --git a/apps/web/src/app/(home)/_components/CodeContainer.tsx b/apps/web/src/app/(home)/_components/CodeContainer.tsx index f3d44f3..fef43e2 100644 --- a/apps/web/src/app/(home)/_components/CodeContainer.tsx +++ b/apps/web/src/app/(home)/_components/CodeContainer.tsx @@ -53,15 +53,15 @@ const CodeContainer = () => { }, [typingComplete, currentStep]); return ( -
+
-
+
-
+
Quick Install Terminal
@@ -69,7 +69,7 @@ const CodeContainer = () => {
-
+
-
+
$ {commands[selectedPM]} @@ -151,7 +151,7 @@ const CodeContainer = () => { whileTap={{ scale: 0.95 }} type="button" onClick={() => copyToClipboard(selectedPM)} - className="text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors" + className="text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors flex-shrink-0 ml-2" title="Copy command" > {copied ? ( @@ -167,8 +167,12 @@ const CodeContainer = () => {
{typingComplete && ( - -
+ +
{currentStep >= 1 && ( { initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 0.3 }} - className="mt-3 pl-4" + className="mt-3 pl-2 sm:pl-4" > {currentStep >= 3 && ( { transition={{ delay: 0.4 }} className="text-blue-600 dark:text-blue-400 flex items-center" > - + Completed - Creating project structure + Creating project structure )} {currentStep >= 4 && ( @@ -245,10 +249,10 @@ const CodeContainer = () => { transition={{ delay: 0.5 }} className="text-blue-600 dark:text-blue-400 flex items-center" > - + Completed - Installing dependencies + Installing dependencies )} {currentStep >= 5 && ( @@ -263,10 +267,10 @@ const CodeContainer = () => { transition={{ delay: 0.7 }} className="text-blue-600 dark:text-blue-400 flex items-center" > - + Completed - Setting up database schema + Setting up database schema { transition={{ delay: 0.8 }} className="text-blue-600 dark:text-blue-400 flex items-center" > - + Completed - Configuring authentication + Configuring authentication { - - Project ready! Run{" "} - +
+ Project ready! Run + cd my-better-t-app - {" "} - and{" "} - + + and + {selectedPM === "npm" && "npm run dev"} {selectedPM === "pnpm" && "pnpm dev"} {selectedPM === "bun" && "bun dev"} - +
)} @@ -329,11 +333,11 @@ const CodeContainer = () => {
-
-
- +
+
+ For custom options, use - + {selectedPM === "npm" && "npx"} {selectedPM === "pnpm" && "pnpm dlx"} {selectedPM === "bun" && "bunx"} create-better-t-stack diff --git a/apps/web/src/app/(home)/_components/Testimonials.tsx b/apps/web/src/app/(home)/_components/Testimonials.tsx index 627fcad..2dd76c5 100644 --- a/apps/web/src/app/(home)/_components/Testimonials.tsx +++ b/apps/web/src/app/(home)/_components/Testimonials.tsx @@ -1,124 +1,184 @@ -import { useRef } from "react"; +"use client"; -const testimonials = [ - { - name: "$ user@dev.sh", - role: "Senior", - company: "TypeScript", - avatar: ">_", - content: - "The type safety across the entire stack is exactly what I've been looking for. Incredible productivity boost.", - }, - { - name: "$ sarah@code.io", - role: "Lead", - company: "Engineer", - avatar: "~/", - content: - "Better-T Stack simplified our deployment pipeline and improved our team's development experience tremendously.", - }, - { - name: "$ alex@terminal.dev", - role: "Full-Stack", - company: "Dev", - avatar: "[]", - content: - "After switching to Better-T, our build times dropped by 60% and bug reports decreased significantly.", - }, +import { motion } from "framer-motion"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import { useEffect, useState } from "react"; +import { Tweet } from "react-tweet"; + +const TWEET_IDS = [ + "1904144343125860404", + "1904215768272654825", + "1904233896851521980", + "1904228496144269699", + "1904301540422070671", + "1904338606409531710", + "1904241046898556970", + "1904318186750652606", + "1904179661086556412", + "1906149740095705265", + "1906001923456790710", ]; -const Testimonials = () => { - const scrollContainerRef = useRef(null); +export default function Testimonials() { + const [startIndex, setStartIndex] = useState(0); + const [tweetsPerPage, setTweetsPerPage] = useState(3); - const duplicatedTestimonials = [...testimonials]; + useEffect(() => { + const handleResize = () => { + if (window.innerWidth >= 1280) { + setTweetsPerPage(6); + } else if (window.innerWidth >= 768) { + setTweetsPerPage(4); + } else if (window.innerWidth >= 640) { + setTweetsPerPage(2); + } else { + setTweetsPerPage(1); + } + }; + + handleResize(); + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, []); + + // Get visible tweets + const getVisibleTweets = () => { + const visible = []; + for (let i = 0; i < tweetsPerPage; i++) { + const index = (startIndex + i) % TWEET_IDS.length; + visible.push(index); + } + return visible; + }; + + const handleNext = () => { + setStartIndex((prev) => (prev + tweetsPerPage) % TWEET_IDS.length); + }; + + const handlePrev = () => { + setStartIndex((prev) => { + const newIndex = prev - tweetsPerPage; + return newIndex < 0 ? TWEET_IDS.length + newIndex : newIndex; + }); + }; + + const visibleTweets = getVisibleTweets(); + const totalPages = Math.ceil(TWEET_IDS.length / tweetsPerPage); + const currentPage = Math.floor(startIndex / tweetsPerPage) + 1; return ( -
-
- -
-
-

- $ cat - testimonials.log -

-

- Output: Users reporting - success with Better-T Stack -

-
- -
-
-
- {duplicatedTestimonials.map((testimonial) => ( -
-
-
-
- {testimonial.avatar} -
-
-
-

- {testimonial.name} -

-

- - {testimonial.role} - - @ - - {testimonial.company} - -

-
-
-

- {testimonial.content} -

-
- ))} -
- -
-
- -
-
- $ - - Join the growing community of developers +
+
+ +

+ + {">"} -

-
+ + Developer Feedback + + +
+ + + + what devs are saying about Better-T-Stack +
- + +
+
+
+
+
+
+
+
+ Developer Feedback Terminal +
+
+ + + + + + + +
+
+ +
+
+ {visibleTweets.map((tweetIndex) => ( + + ))} +
+
+ +
+
+ + Page {currentPage} of {totalPages} + +
+ +
+
+ {Array.from({ length: totalPages }).map((_, i) => { + const isActive = i === currentPage - 1; + return ( +
+
+
+
+ +
+
); -}; - -export default Testimonials; +} diff --git a/apps/web/src/app/(home)/page.tsx b/apps/web/src/app/(home)/page.tsx index 574adb0..059b4ab 100644 --- a/apps/web/src/app/(home)/page.tsx +++ b/apps/web/src/app/(home)/page.tsx @@ -5,38 +5,38 @@ import BackgroundGradients from "./_components/BackgroundGradients"; import CodeContainer from "./_components/CodeContainer"; import CustomizableSection from "./_components/CustomizableSection"; import NpmPackage from "./_components/NpmPackage"; -// import Testimonials from "./_components/Testimonials"; +import Testimonials from "./_components/Testimonials"; export default function HomePage() { return ( -
+
-
-
-
-

+
+
+
+

Better-T Stack

-
+
-

+

A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations

-
+
@@ -45,8 +45,10 @@ export default function HomePage() {
+ -
+ +
@@ -54,11 +56,11 @@ export default function HomePage() {
-
-
+
+
@@ -81,7 +83,8 @@ export default function HomePage() {
- {/* */} + +

); } diff --git a/apps/web/src/app/global.css b/apps/web/src/app/global.css index aa185c2..a89ab7c 100644 --- a/apps/web/src/app/global.css +++ b/apps/web/src/app/global.css @@ -10,6 +10,10 @@ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%' height='100%' filter='url(%23noise)' opacity='0.4'/%3E%3C/svg%3E"); } +.react-tweet-theme { + --tweet-container-margin: 0 !important; +} + .shiny-text { background: linear-gradient( 120deg, diff --git a/bun.lock b/bun.lock index d579a7b..eb27874 100644 --- a/bun.lock +++ b/bun.lock @@ -14,7 +14,7 @@ }, "apps/cli": { "name": "create-better-t-stack", - "version": "1.2.0", + "version": "1.3.3", "bin": { "create-better-t-stack": "dist/index.js", }, @@ -44,11 +44,12 @@ "fumadocs-core": "15.1.2", "fumadocs-mdx": "11.5.7", "fumadocs-ui": "15.1.2", - "lucide-react": "^0.483.0", + "lucide-react": "^0.485.0", "motion": "^12.5.0", "next": "15.2.3", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-tweet": "^3.2.2", }, "devDependencies": { "@tailwindcss/postcss": "^4.0.15", @@ -1113,7 +1114,7 @@ "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "lucide-react": ["lucide-react@0.483.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WldsY17Qb/T3VZdMnVQ9C3DDIP7h1ViDTHVdVGnLZcvHNg30zH/MTQ04RTORjexoGmpsXroiQXZ4QyR0kBy0FA=="], + "lucide-react": ["lucide-react@0.485.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-NvyQJ0LKyyCxL23nPKESlr/jmz8r7fJO1bkuptSNYSy0s8VVj4ojhX0YAgmE1e0ewfxUZjIlZpvH+otfTnla8Q=="], "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], @@ -1365,6 +1366,8 @@ "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + "react-tweet": ["react-tweet@3.2.2", "", { "dependencies": { "@swc/helpers": "^0.5.3", "clsx": "^2.0.0", "swr": "^2.2.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-hIkxAVPpN2RqWoDEbo3TTnN/pDcp9/Jb6pTgiA4EbXa9S+m2vHIvvZKHR+eS0PDIsYqe+zTmANRa5k6+/iwGog=="], + "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="], "readdirp": ["readdirp@4.1.1", "", {}, "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw=="], @@ -1525,6 +1528,8 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + "swr": ["swr@2.3.3", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A=="], + "tailwind-merge": ["tailwind-merge@3.0.2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="], "tailwindcss": ["tailwindcss@4.0.15", "", {}, "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg=="], @@ -1725,6 +1730,8 @@ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "fumadocs-ui/lucide-react": ["lucide-react@0.483.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WldsY17Qb/T3VZdMnVQ9C3DDIP7h1ViDTHVdVGnLZcvHNg30zH/MTQ04RTORjexoGmpsXroiQXZ4QyR0kBy0FA=="], + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],