Project setup and navbar

This commit is contained in:
Braydon 2024-08-29 00:53:55 -04:00
parent 8175c67a11
commit a7d8fe26b9
11 changed files with 391 additions and 5 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -9,10 +9,15 @@
"lint": "next lint"
},
"dependencies": {
"@heroicons/react": "^2.1.5",
"@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-slot": "^1.1.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"framer-motion": "^11.3.30",
"lucide-react": "^0.436.0",
"next": "14.2.3",
"next-themes": "^0.3.0",
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.5.2",

BIN
public/me.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

View File

@ -1,6 +1,8 @@
import type { Metadata, Viewport } from "next";
import { Inter } from "next/font/google";
import { ThemeProvider } from "@/components/theme-provider";
import "../globals.css";
import { ReactElement } from "react";
const inter = Inter({ subsets: ["latin"] });
@ -23,9 +25,18 @@ const RootLayout = ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => (
}>): ReactElement => (
<html lang="en">
<body className={inter.className}>{children}</body>
<body className={inter.className}>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
);
export default RootLayout;

View File

@ -1,4 +1,10 @@
import Navbar from "@/components/landing/navbar";
import { ReactElement } from "react";
const LandingPage = (): ReactElement => <main>Hello World</main>;
const LandingPage = (): ReactElement => (
<main className="flex flex-col">
<Navbar />
Page Content
</main>
);
export default LandingPage;

View File

@ -4,7 +4,36 @@
@layer base {
:root {
--background: 240 10% 3.9%;
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--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 72.22% 50.59%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5% 64.9%;
--radius: 0.5rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
.dark {
--background: 0 0% 0%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
@ -19,10 +48,11 @@
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--destructive-foreground: 0 85.7% 97.3%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;

View File

@ -0,0 +1,103 @@
import Image from "next/image";
import Link from "next/link";
import { ReactElement } from "react";
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuIndicator,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
NavigationMenuViewport,
} from "@/components/ui/navigation-menu";
import { navigationMenuTriggerStyle } from "@/components/ui/navigation-menu";
import { HeartIcon } from "@heroicons/react/24/solid";
import { cn } from "@/lib/utils";
import ThemeSwitcher from "./theme-switcher";
import { Button } from "../ui/button";
import { BookOpenIcon } from "@heroicons/react/24/outline";
import { SignalIcon } from "@heroicons/react/24/outline";
const Navbar = (): ReactElement => (
<nav className="py-4 flex gap-14 justify-center items-center border-b">
<Branding />
<Links />
</nav>
);
const Branding = (): ReactElement => (
<Link
className="flex gap-4 items-center hover:opacity-75 transition-all transform-gpu"
href="/"
>
<Image
className="rounded-full"
src="/me.png"
alt="My Selfie (:"
width={40}
height={40}
/>
<h1 className="text-xl font-bold">RainnnyCLUB</h1>
</Link>
);
const Links = (): ReactElement => (
<NavigationMenu>
<NavigationMenuList>
{/* Useful Links */}
<NavigationMenuItem>
<NavigationMenuTrigger>Useful Links</NavigationMenuTrigger>
<NavigationMenuContent>
<NavigationMenuLink>
<UsefulLinksContent />
</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
{/* Donate */}
<NavigationMenuItem>
<Link href="https://buymeacoffee.com/Rainnny7" legacyBehavior passHref>
<NavigationMenuLink
className={cn(navigationMenuTriggerStyle(), "gap-2")}
target="_blank"
>
<span>Donate</span>
<HeartIcon
className="text-red-500 animate-pulse"
width={20}
height={20}
/>
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
{/* Theme Switcher */}
<NavigationMenuItem>
<ThemeSwitcher />
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
);
const UsefulLinksContent = (): ReactElement => (
<div className="p-3 flex gap-5">
{/* Wiki */}
<Link href="https://docs.rainnny.club" target="_blank">
<Button className="gap-3" variant="ghost">
<BookOpenIcon width={24} height={24} />
<span>Wiki</span>
</Button>
</Link>
{/* Status Page */}
<Link href="https://status.rainnny.club" target="_blank">
<Button className="gap-3" variant="ghost">
<SignalIcon width={24} height={24} />
<span>Service Status</span>
</Button>
</Link>
</div>
);
export default Navbar;

View File

@ -0,0 +1,38 @@
"use client";
import { ReactElement } from "react";
import { MoonStar, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
import { motion } from "framer-motion";
import { UseThemeProps } from "next-themes/dist/types";
const ThemeSwitcher = (): ReactElement => {
const { theme, setTheme }: UseThemeProps = useTheme();
const isLight = theme === "light";
return (
<Button
className="mx-7 px-5 py-1.5 flex items-center relative hover:opacity-85"
variant="ghost"
onClick={() => setTheme(isLight ? "dark" : "light")}
>
<motion.div
initial={{ rotate: 0, scale: 1 }}
animate={{ rotate: isLight ? 0 : -90, scale: isLight ? 1 : 0 }}
transition={{ duration: 0.5 }}
className="absolute"
>
<Sun className="w-[1.2rem] h-[1.2rem]" />
</motion.div>
<motion.div
initial={{ rotate: 90, scale: 0 }}
animate={{ rotate: isLight ? 90 : 0, scale: isLight ? 0 : 1 }}
transition={{ duration: 0.5 }}
className="absolute"
>
<MoonStar className="w-[1.2rem] h-[1.2rem]" />
</motion.div>
</Button>
);
};
export default ThemeSwitcher;

View File

@ -0,0 +1,9 @@
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

View File

@ -0,0 +1,56 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@ -0,0 +1,128 @@
import * as React from "react";
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
import { cva } from "class-variance-authority";
import { ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
const NavigationMenu = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Root
ref={ref}
className={cn(
"relative z-10 flex max-w-max flex-1 items-center justify-center",
className,
)}
{...props}
>
{children}
<NavigationMenuViewport />
</NavigationMenuPrimitive.Root>
));
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName;
const NavigationMenuList = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.List>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.List
ref={ref}
className={cn(
"group flex flex-1 list-none items-center justify-center space-x-1",
className,
)}
{...props}
/>
));
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
const NavigationMenuItem = NavigationMenuPrimitive.Item;
const navigationMenuTriggerStyle = cva(
"group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50",
);
const NavigationMenuTrigger = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Trigger
ref={ref}
className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props}
>
{children}{" "}
<ChevronDown
className="relative top-[1px] ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
aria-hidden="true"
/>
</NavigationMenuPrimitive.Trigger>
));
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName;
const NavigationMenuContent = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Content
ref={ref}
className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
className,
)}
{...props}
/>
));
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName;
const NavigationMenuLink = NavigationMenuPrimitive.Link;
const NavigationMenuViewport = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
>(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
className,
)}
ref={ref}
{...props}
/>
</div>
));
NavigationMenuViewport.displayName =
NavigationMenuPrimitive.Viewport.displayName;
const NavigationMenuIndicator = React.forwardRef<
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
>(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Indicator
ref={ref}
className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
className,
)}
{...props}
>
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
</NavigationMenuPrimitive.Indicator>
));
NavigationMenuIndicator.displayName =
NavigationMenuPrimitive.Indicator.displayName;
export {
navigationMenuTriggerStyle,
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator,
NavigationMenuViewport,
};