Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NotificationProvider } from "../contexts/NotificationContext";
import { SocialProvider } from "../contexts/SocialContext";
import { LiveNotificationToast } from "../components/notifications/LiveNotificationToast";
import { PageTransition } from "../components/layout/PageTransition";
import { ThemeProvider } from "../contexts/ThemeProvider";
import "../styles/globals.css";

export const metadata: Metadata = {
Expand All @@ -18,18 +19,25 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className="bg-black text-white min-h-screen flex flex-col">
<NotificationProvider>
<SocialProvider>
<Header />
<LiveNotificationToast />
<PageTransition className="flex-1 max-w-7xl mx-auto px-4 py-6 pt-16">
{children}
</PageTransition>
<Footer />
</SocialProvider>
</NotificationProvider>
<html lang="en" suppressHydrationWarning>
<body className={`${inter.className} bg-background text-foreground min-h-screen flex flex-col transition-colors duration-300`}>
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
<NotificationProvider>
<SocialProvider>
<Header />
<LiveNotificationToast />
<PageTransition className="flex-1 max-w-7xl mx-auto px-4 py-6 pt-16">
{children}
</PageTransition>
<Footer />
</SocialProvider>
</NotificationProvider>
</ThemeProvider>
</body>
</html>
);
Expand Down
21 changes: 12 additions & 9 deletions frontend/src/components/ui/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from "react";

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "danger";
size?: "sm" | "md" | "lg";
variant?: "primary" | "secondary" | "danger" | "ghost" | "outline";
size?: "sm" | "md" | "lg" | "icon";
}

const Button: React.FC<ButtonProps> = ({
Expand All @@ -12,17 +12,20 @@ const Button: React.FC<ButtonProps> = ({
...props
}) => {
const base =
"font-semibold rounded transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 inline-flex items-center justify-center";
"font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 inline-flex items-center justify-center disabled:opacity-50 disabled:pointer-events-none active:scale-95";
const sizes = {
sm: "px-3 py-2 text-sm min-h-[44px] md:px-3 md:py-1 md:min-h-0",
md: "px-4 py-3 text-base min-h-[48px] md:px-4 md:py-2 md:min-h-0",
lg: "px-6 py-4 text-lg min-h-[52px] md:px-6 md:py-3 md:min-h-0",
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-sm",
lg: "px-6 py-3 text-base",
icon: "h-9 w-9",
};
const variants = {
primary:
"bg-purple-700 text-white hover:bg-purple-600 focus:ring-purple-500",
secondary: "bg-muted text-foreground hover:bg-muted/80 focus:ring-muted",
danger: "bg-red-600 text-white hover:bg-red-500 focus:ring-red-400",
"bg-primary text-primary-foreground hover:opacity-90 shadow-sm",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
danger: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
ghost: "bg-transparent hover:bg-accent hover:text-accent-foreground",
outline: "bg-transparent border border-input hover:bg-accent hover:text-accent-foreground",
};

// Allow custom className to override variants
Expand Down
23 changes: 23 additions & 0 deletions frontend/src/components/ui/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import * as React from "react";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "./index";

export function ThemeToggle() {
const { setTheme, theme } = useTheme();

return (
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
className="relative h-9 w-9 rounded-md border border-white/10 bg-white/5 transition-colors hover:bg-white/10 dark:hover:bg-black/20"
>
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
);
}
9 changes: 9 additions & 0 deletions frontend/src/contexts/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes";

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
71 changes: 40 additions & 31 deletions frontend/src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,50 @@

@layer base {
:root {
/* Light mode */
/* Light mode - Clean Slate */
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 96.5%;
--card-foreground: 222.2 84% 4.9%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 217.2 91.2% 59.8%;
--accent-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 217.2 91.2% 59.8%;
--foreground: 224 71.4% 4.1%;
--card: 0 0% 100%;
--card-foreground: 224 71.4% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 224 71.4% 4.1%;
--primary: 262.1 83.3% 57.8%;
--primary-foreground: 210 20% 98%;
--secondary: 220 14.3% 95.9%;
--secondary-foreground: 220.9 39.3% 11%;
--muted: 220 14.3% 95.9%;
--muted-foreground: 220 8.9% 46.1%;
--accent: 262.1 83.3% 57.8%;
--accent-foreground: 210 20% 98%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 20% 98%;
--border: 220 13% 91%;
--input: 220 13% 91%;
--ring: 262.1 83.3% 57.8%;
--radius: 0.75rem;
}

.dark {
/* Dark mode */
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 224.3 76.3% 48%;
/* Dark mode - Deep Space/Purple */
--background: 224 71.4% 4.1%;
--foreground: 210 20% 98%;
--card: 224 71.4% 4.1%;
--card-foreground: 210 20% 98%;
--popover: 224 71.4% 4.1%;
--popover-foreground: 210 20% 98%;
--primary: 263.4 70% 50.4%;
--primary-foreground: 210 20% 98%;
--secondary: 215 27.9% 16.9%;
--secondary-foreground: 210 20% 98%;
--muted: 215 27.9% 16.9%;
--muted-foreground: 217.9 10.6% 64.9%;
--accent: 215 27.9% 16.9%;
--accent-foreground: 210 20% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 20% 98%;
--border: 215 27.9% 16.9%;
--input: 215 27.9% 16.9%;
--ring: 263.4 70% 50.4%;
}
}

Expand Down
Loading