From 49802c07db63c41bd9b3c50b118d8cb69b805ed5 Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 3 Mar 2026 18:42:25 -0500 Subject: [PATCH 01/25] feat(web): add Hero section with Terminal Chic theme and typewriter animation - Set up CSS variables in globals.css for Terminal Chic theme (light/dark modes) - Add JetBrains Mono font alongside Inter for terminal-style headlines - Create Hero.tsx component with: - Typewriter effect for '> volvox-bot' headline - Blinking green terminal cursor - Subheadline fade-in after typing completes - CTA buttons with slide-up animation - Mock chat preview showing bot interaction - Update page.tsx with new Hero component and scroll-triggered animations - Install framer-motion for animations - Update landing page tests to match new structure --- pnpm-lock.yaml | 38 +++++ web/package.json | 1 + web/src/app/globals.css | 118 ++++++++++----- web/src/app/layout.tsx | 24 ++- web/src/app/page.tsx | 121 +++++++++------ web/src/components/landing/Hero.tsx | 222 ++++++++++++++++++++++++++++ web/tests/app/landing.test.tsx | 79 ++++++---- 7 files changed, 492 insertions(+), 111 deletions(-) create mode 100644 web/src/components/landing/Hero.tsx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cee4d8562..b29c06a7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,6 +106,9 @@ importers: diff: specifier: ^8.0.3 version: 8.0.3 + framer-motion: + specifier: ^12.34.5 + version: 12.34.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) lucide-react: specifier: ^0.525.0 version: 0.525.0(react@19.2.4) @@ -3273,6 +3276,20 @@ packages: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} + framer-motion@12.34.5: + resolution: {integrity: sha512-Z2dQ+o7BsfpJI3+u0SQUNCrN+ajCKJen1blC4rCHx1Ta2EOHs+xKJegLT2aaD9iSMbU3OoX+WabQXkloUbZmJQ==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + fresh@2.0.0: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} @@ -3950,6 +3967,12 @@ packages: moment@2.30.1: resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + motion-dom@12.34.5: + resolution: {integrity: sha512-k33CsnxO2K3gBRMUZT+vPmc4Utlb5menKdG0RyVNLtlqRaaJPRWlE9fXl8NTtfZ5z3G8TDvqSu0MENLqSTaHZA==} + + motion-utils@12.29.2: + resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -8288,6 +8311,15 @@ snapshots: forwarded@0.2.0: {} + framer-motion@12.34.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + motion-dom: 12.34.5 + motion-utils: 12.29.2 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + fresh@2.0.0: {} fs-constants@1.0.0: {} @@ -9022,6 +9054,12 @@ snapshots: moment@2.30.1: {} + motion-dom@12.34.5: + dependencies: + motion-utils: 12.29.2 + + motion-utils@12.29.2: {} + ms@2.1.3: {} mustache@4.2.0: {} diff --git a/web/package.json b/web/package.json index 11776e144..97a89f00d 100644 --- a/web/package.json +++ b/web/package.json @@ -22,6 +22,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "diff": "^8.0.3", + "framer-motion": "^12.34.5", "lucide-react": "^0.525.0", "next": "^16.1.6", "next-auth": "^4.24.13", diff --git a/web/src/app/globals.css b/web/src/app/globals.css index b845b3169..d68221827 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -1,6 +1,24 @@ @import "tailwindcss"; +/* JetBrains Mono for headlines, Inter for body (loaded in layout.tsx) */ @theme { + --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + + /* Terminal Chic Colors */ + --color-terminal-green: #22c55e; + --color-terminal-cursor: #22c55e; + --color-terminal-bg: #0a0a0a; + --color-terminal-border: #1f1f1f; + --color-terminal-text: #e4e4e7; + --color-terminal-muted: #71717a; + + /* Light mode Terminal Chic */ + --color-terminal-light-bg: #fafafa; + --color-terminal-light-border: #e4e4e7; + --color-terminal-light-text: #18181b; + --color-terminal-light-muted: #71717a; + + /* Existing shadcn colors */ --color-border: hsl(var(--border)); --color-input: hsl(var(--input)); --color-ring: hsl(var(--ring)); @@ -25,8 +43,13 @@ --radius-lg: var(--radius); --radius-md: calc(var(--radius) - 2px); --radius-sm: calc(var(--radius) - 4px); + + /* Animations */ --animate-accordion-down: accordion-down 0.2s ease-out; --animate-accordion-up: accordion-up 0.2s ease-out; + --animate-blink: blink 1s step-end infinite; + --animate-typewriter: typewriter 0.1s steps(1) forwards; + @keyframes accordion-down { from { height: 0; @@ -43,52 +66,62 @@ height: 0; } } + @keyframes blink { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0; + } + } } @layer base { :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; + /* Terminal Chic Light Mode */ + --background: 0 0% 98%; + --foreground: 240 10% 10%; --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; + --card-foreground: 240 10% 10%; --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - --destructive: 0 84.2% 60.2%; + --popover-foreground: 240 10% 10%; + --primary: 142 76% 36%; + --primary-foreground: 0 0% 100%; + --secondary: 240 5% 96%; + --secondary-foreground: 240 10% 10%; + --muted: 240 5% 96%; + --muted-foreground: 240 4% 46%; + --accent: 240 5% 96%; + --accent-foreground: 240 10% 10%; + --destructive: 0 84% 60%; --destructive-foreground: 0 0% 98%; - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 5.9% 10%; + --border: 240 6% 90%; + --input: 240 6% 90%; + --ring: 142 76% 36%; --radius: 0.5rem; } .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; + /* Terminal Chic Dark Mode */ + --background: 0 0% 4%; + --foreground: 0 0% 89%; + --card: 0 0% 6%; + --card-foreground: 0 0% 89%; + --popover: 0 0% 6%; + --popover-foreground: 0 0% 89%; + --primary: 142 76% 46%; + --primary-foreground: 0 0% 100%; + --secondary: 0 0% 12%; + --secondary-foreground: 0 0% 89%; + --muted: 0 0% 12%; + --muted-foreground: 0 0% 46%; + --accent: 0 0% 12%; + --accent-foreground: 0 0% 89%; + --destructive: 0 62% 30%; --destructive-foreground: 0 0% 98%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; + --border: 0 0% 12%; + --input: 0 0% 12%; + --ring: 142 76% 46%; } } @@ -97,6 +130,21 @@ @apply border-border; } body { - @apply bg-background text-foreground; + @apply bg-background text-foreground antialiased; + font-feature-settings: "rlig" 1, "calt" 1; + } +} + +/* Terminal cursor blink animation */ +.terminal-cursor { + animation: blink 1s step-end infinite; +} + +@keyframes blink { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0; } } diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index 389047e00..307fbca42 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -1,20 +1,32 @@ import type { Metadata } from 'next'; -import { Inter } from 'next/font/google'; +import { Inter, JetBrains_Mono } from 'next/font/google'; import { Providers } from '@/components/providers'; import './globals.css'; -const inter = Inter({ subsets: ['latin'] }); +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', +}); + +const jetbrainsMono = JetBrains_Mono({ + subsets: ['latin'], + variable: '--font-mono', +}); export const metadata: Metadata = { - title: 'Bill Bot Dashboard', + title: 'Volvox Bot - AI-Powered Discord Bot', description: - 'Manage your Bill Bot Discord server — moderation, AI chat, configuration, and more.', + 'The AI-powered Discord bot for modern communities. Moderation, AI chat, dynamic welcomes, spam detection, and a fully configurable web dashboard.', }; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - - + + {children} diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index 43838426b..fc05b31a5 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -1,5 +1,10 @@ +'use client'; + +import { motion } from 'framer-motion'; import { Bot, MessageSquare, Shield, Sparkles, Users, Zap } from 'lucide-react'; import Link from 'next/link'; +import { Hero } from '@/components/landing/Hero'; +import { ThemeToggle } from '@/components/theme-toggle'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { getBotInviteUrl } from '@/lib/discord'; @@ -63,11 +68,12 @@ export default function LandingPage() {
- B + V
- Bill Bot + volvox-bot
- {/* Hero */} -
-
- B -
-

Bill Bot

-

- The AI-powered Discord bot for the Volvox community. Moderation, AI chat, dynamic - welcomes, spam detection, and a fully configurable web dashboard. -

-
- - -
-
+ {/* Hero Section */} + {/* Features */}
-

Everything you need

-

+ + > Features + + A full-featured Discord bot with a modern web dashboard. -

+
- {features.map((feature) => ( - - -
- -
- {feature.title} -
- - - {feature.description} - - -
+ {features.map((feature, index) => ( + + + +
+ +
+ {feature.title} +
+ + + {feature.description} + + +
+
))}
@@ -124,23 +137,43 @@ export default function LandingPage() { {/* CTA */}
-

Ready to get started?

-

- Add Bill Bot to your Discord server and manage everything from this dashboard. -

- + + > Ready to get started? + + + Add Volvox Bot to your Discord server and manage everything from this dashboard. + + + +
{/* Footer */}