diff options
-rw-r--r-- | frontend/src/app/globals.css | 390 | ||||
-rw-r--r-- | frontend/src/app/layout.tsx | 29 | ||||
-rw-r--r-- | frontend/src/app/page.tsx | 20 | ||||
-rw-r--r-- | frontend/src/components/footer.tsx | 96 | ||||
-rw-r--r-- | frontend/src/components/header.tsx | 110 | ||||
-rw-r--r-- | frontend/src/components/hero-section.tsx | 20 | ||||
-rw-r--r-- | frontend/src/components/product-card.tsx | 52 | ||||
-rw-r--r-- | frontend/src/components/theme-provider.tsx | 9 | ||||
-rw-r--r-- | frontend/src/components/theme-toggle.tsx | 50 |
9 files changed, 672 insertions, 104 deletions
diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 92fb855..89de65b 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -120,3 +120,393 @@ @apply bg-background text-foreground; } } + +/* Complete override for ghost variant default styles */ +/* This targets the specific ghost variant classes from the button component */ +.nav-button-transparent { + background: transparent !important; + background-color: transparent !important; + background-image: none !important; + border: none !important; + border-color: transparent !important; + box-shadow: none !important; + --tw-bg-opacity: 0 !important; + --accent: transparent !important; + --background: transparent !important; + --tw-border-opacity: 0 !important; + --tw-shadow: none !important; + --tw-ring-shadow: none !important; + --tw-ring-offset-shadow: none !important; + cursor: pointer !important; +} + +/* Force override ghost variant styles completely */ +button[class*="nav-button-transparent"], +[data-slot="button"][class*="nav-button-transparent"], +[data-slot="navigation-menu-trigger"][class*="nav-button-transparent"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; + box-shadow: none !important; +} + +/* Override ghost variant hover specifically */ +button[class*="nav-button-transparent"][class*="hover:bg-accent"], +[data-slot="button"][class*="nav-button-transparent"][class*="hover:bg-accent"], +[data-slot="navigation-menu-trigger"][class*="nav-button-transparent"][class*="hover:bg-accent"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Dark mode ghost variant overrides */ +.dark button[class*="nav-button-transparent"][class*="dark:hover:bg-accent"], +.dark [data-slot="button"][class*="nav-button-transparent"][class*="dark:hover:bg-accent"], +.dark [data-slot="navigation-menu-trigger"][class*="nav-button-transparent"][class*="dark:hover:bg-accent"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Remove focus rings and outlines for nav buttons */ +.nav-button-transparent:focus, +.nav-button-transparent:focus-visible { + outline: none !important; + ring: none !important; + --tw-ring-shadow: none !important; + border: none !important; + background: transparent !important; + background-color: transparent !important; +} + +/* Light mode subtle hover effects - only show on hover */ +.nav-button-transparent:hover { + background: rgba(0, 0, 0, 0.08) !important; + background-color: rgba(0, 0, 0, 0.08) !important; + --tw-bg-opacity: 1 !important; + border: none !important; + transition: background-color 0.2s ease-in-out !important; +} + +/* Dark mode even more subtle hover effects */ +.dark .nav-button-transparent:hover { + background: rgba(255, 255, 255, 0.06) !important; + background-color: rgba(255, 255, 255, 0.06) !important; + transition: background-color 0.2s ease-in-out !important; +} + +/* Override NavigationMenuTrigger specific classes with maximum specificity */ +.nav-button-transparent.bg-background { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Focus states - only subtle background, no borders or rings */ +.nav-button-transparent:focus:not(:hover) { + background: rgba(0, 0, 0, 0.04) !important; + background-color: rgba(0, 0, 0, 0.04) !important; + border: none !important; + box-shadow: none !important; +} + +.dark .nav-button-transparent:focus:not(:hover) { + background: rgba(255, 255, 255, 0.03) !important; + background-color: rgba(255, 255, 255, 0.03) !important; + border: none !important; + box-shadow: none !important; +} + +/* Override open states */ +.nav-button-transparent[data-state="open"] { + background: rgba(0, 0, 0, 0.08) !important; + background-color: rgba(0, 0, 0, 0.08) !important; + border: none !important; +} + +.dark .nav-button-transparent[data-state="open"] { + background: rgba(255, 255, 255, 0.06) !important; + background-color: rgba(255, 255, 255, 0.06) !important; + border: none !important; +} + +/* Ensure all shadcn/ui utility classes are overridden with high specificity */ +button.nav-button-transparent, +[data-slot="button"].nav-button-transparent, +[data-slot="navigation-menu-trigger"].nav-button-transparent { + background: transparent !important; + background-color: transparent !important; + border: none !important; + border-color: transparent !important; + box-shadow: none !important; + cursor: pointer !important; +} + +/* Hover states for all button types */ +button.nav-button-transparent:hover, +[data-slot="button"].nav-button-transparent:hover, +[data-slot="navigation-menu-trigger"].nav-button-transparent:hover { + background: rgba(0, 0, 0, 0.08) !important; + background-color: rgba(0, 0, 0, 0.08) !important; + border: none !important; + transition: background-color 0.2s ease-in-out !important; + cursor: pointer !important; +} + +/* Focus states for all button types */ +button.nav-button-transparent:focus, +[data-slot="button"].nav-button-transparent:focus, +[data-slot="navigation-menu-trigger"].nav-button-transparent:focus { + background: rgba(0, 0, 0, 0.04) !important; + background-color: rgba(0, 0, 0, 0.04) !important; + border: none !important; + box-shadow: none !important; + outline: none !important; + cursor: pointer !important; +} + +/* Dark mode hover and focus states */ +.dark button.nav-button-transparent:hover, +.dark [data-slot="button"].nav-button-transparent:hover, +.dark [data-slot="navigation-menu-trigger"].nav-button-transparent:hover { + background: rgba(255, 255, 255, 0.06) !important; + background-color: rgba(255, 255, 255, 0.06) !important; + border: none !important; + transition: background-color 0.2s ease-in-out !important; + cursor: pointer !important; +} + +.dark button.nav-button-transparent:focus, +.dark [data-slot="button"].nav-button-transparent:focus, +.dark [data-slot="navigation-menu-trigger"].nav-button-transparent:focus { + background: rgba(255, 255, 255, 0.03) !important; + background-color: rgba(255, 255, 255, 0.03) !important; + border: none !important; + box-shadow: none !important; + outline: none !important; + cursor: pointer !important; +} + +/* Dropdown and navigation item transparency */ +.nav-dropdown-transparent { + background: rgba(255, 255, 255, 0.98) !important; + backdrop-filter: blur(16px) saturate(180%) !important; + border: 1px solid rgba(255, 255, 255, 0.3) !important; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1) !important; +} + +.dark .nav-dropdown-transparent { + background: rgba(23, 23, 23, 0.98) !important; + border: 1px solid rgba(82, 82, 82, 0.4) !important; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4) !important; +} + +.nav-dropdown-item { + background: transparent !important; + border: none !important; + transition: background-color 0.2s ease-in-out !important; +} + +.nav-dropdown-item:hover { + background: rgba(0, 0, 0, 0.08) !important; + border: none !important; +} + +.dark .nav-dropdown-item:hover { + background: rgba(255, 255, 255, 0.08) !important; + border: none !important; +} + +/* Additional overrides for complete transparency */ +/* Handle any remaining shadcn/ui styling conflicts */ +.nav-button-transparent[data-variant="ghost"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; + cursor: pointer !important; +} + +.nav-button-transparent[data-variant="ghost"]:hover { + background: rgba(0, 0, 0, 0.08) !important; + background-color: rgba(0, 0, 0, 0.08) !important; + border: none !important; + cursor: pointer !important; +} + +.dark .nav-button-transparent[data-variant="ghost"]:hover { + background: rgba(255, 255, 255, 0.06) !important; + background-color: rgba(255, 255, 255, 0.06) !important; + border: none !important; + cursor: pointer !important; +} + +/* Override any remaining background utilities */ +.nav-button-transparent.bg-transparent, +.nav-button-transparent.bg-accent, +.nav-button-transparent.hover\:bg-accent, +.nav-button-transparent.focus\:bg-accent { + background: transparent !important; + background-color: transparent !important; + border: none !important; + cursor: pointer !important; +} + +/* Ensure link elements in navigation also have transparent styling */ +a.nav-button-transparent { + background: transparent !important; + background-color: transparent !important; + border: none !important; + text-decoration: none !important; + cursor: pointer !important; +} + +a.nav-button-transparent:hover { + background: rgba(0, 0, 0, 0.08) !important; + background-color: rgba(0, 0, 0, 0.08) !important; + border: none !important; + text-decoration: none !important; + cursor: pointer !important; +} + +.dark a.nav-button-transparent:hover { + background: rgba(255, 255, 255, 0.06) !important; + background-color: rgba(255, 255, 255, 0.06) !important; + border: none !important; + text-decoration: none !important; + cursor: pointer !important; +} + +/* Ultra-specific overrides to ensure transparency takes precedence */ +/* Handle all possible combinations of button classes */ +button.nav-button-transparent.hover\:bg-accent, +button.nav-button-transparent.dark\:hover\:bg-accent\/50, +button.nav-button-transparent[class*="hover:bg"], +[data-slot="button"].nav-button-transparent.hover\:bg-accent, +[data-slot="button"].nav-button-transparent.dark\:hover\:bg-accent\/50, +[data-slot="button"].nav-button-transparent[class*="hover:bg"], +[data-slot="navigation-menu-trigger"].nav-button-transparent.hover\:bg-accent, +[data-slot="navigation-menu-trigger"].nav-button-transparent.dark\:hover\:bg-accent\/50, +[data-slot="navigation-menu-trigger"].nav-button-transparent[class*="hover:bg"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Override any CSS custom properties that might affect background */ +.nav-button-transparent { + --tw-bg-accent: transparent !important; + --tw-bg-background: transparent !important; + --tw-bg-muted: transparent !important; + --tw-bg-secondary: transparent !important; +} + +/* Ensure no background on any state for nav buttons */ +.nav-button-transparent, +.nav-button-transparent:not(:hover):not(:focus):not(:active) { + background: transparent !important; + background-color: transparent !important; + box-shadow: none !important; + border: none !important; +} + +/* Maximum specificity overrides - nuclear option for transparency */ +html body header button.nav-button-transparent, +html body header [data-slot="button"].nav-button-transparent, +html body header [data-slot="navigation-menu-trigger"].nav-button-transparent, +html body header div button.nav-button-transparent, +html body header div [data-slot="button"].nav-button-transparent, +html body header div [data-slot="navigation-menu-trigger"].nav-button-transparent { + background: transparent !important; + background-color: transparent !important; + background-image: none !important; + border: none !important; + border-color: transparent !important; + box-shadow: none !important; + backdrop-filter: none !important; +} + +/* Override any inline styles or computed styles */ +.nav-button-transparent[style*="background"] { + background: transparent !important; + background-color: transparent !important; +} + +/* Target specific button classes that might have background */ +.nav-button-transparent.inline-flex, +.nav-button-transparent[class*="inline-flex"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Enhanced link element transparency for Sale button and similar nav links */ +/* Apply maximum specificity for link elements in navigation */ +html body header a.nav-button-transparent, +html body header div a.nav-button-transparent, +html body header nav a.nav-button-transparent, +[data-slot="navigation-menu-link"].nav-button-transparent { + background: transparent !important; + background-color: transparent !important; + background-image: none !important; + border: none !important; + border-color: transparent !important; + box-shadow: none !important; + backdrop-filter: none !important; + cursor: pointer !important; +} + +/* Override any NavigationMenuLink default styles */ +a.nav-button-transparent[data-slot="navigation-menu-link"], +[data-slot="navigation-menu-link"].nav-button-transparent { + background: transparent !important; + background-color: transparent !important; + border: none !important; + box-shadow: none !important; +} + +/* Ensure link hover states maintain transparency in default state */ +a.nav-button-transparent:not(:hover):not(:focus):not(:active) { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Specific overrides for NavigationMenuLink component (Sale button) */ +[data-slot="navigation-menu-link"].nav-button-transparent { + background: transparent !important; + background-color: transparent !important; + border: none !important; + box-shadow: none !important; + cursor: pointer !important; +} + +/* Override NavigationMenuLink hover and focus states */ +[data-slot="navigation-menu-link"].nav-button-transparent.hover\:bg-accent, +[data-slot="navigation-menu-link"].nav-button-transparent.focus\:bg-accent, +[data-slot="navigation-menu-link"].nav-button-transparent[class*="hover:bg-accent"], +[data-slot="navigation-menu-link"].nav-button-transparent[class*="focus:bg-accent"], +[data-slot="navigation-menu-link"].nav-button-transparent[class*="data-[active=true]:bg-accent"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Override NavigationMenuLink data states */ +[data-slot="navigation-menu-link"].nav-button-transparent[data-active="true"] { + background: transparent !important; + background-color: transparent !important; + border: none !important; +} + +/* Ensure NavigationMenuLink has proper hover effect */ +[data-slot="navigation-menu-link"].nav-button-transparent:hover { + background: rgba(0, 0, 0, 0.08) !important; + background-color: rgba(0, 0, 0, 0.08) !important; + border: none !important; + transition: background-color 0.2s ease-in-out !important; +} + +.dark [data-slot="navigation-menu-link"].nav-button-transparent:hover { + background: rgba(255, 255, 255, 0.06) !important; + background-color: rgba(255, 255, 255, 0.06) !important; +} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 50bb899..651acc8 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,9 +1,10 @@ import type { Metadata } from "next"; import "./globals.css"; import { Toaster } from "@/components/ui/sonner"; +import { ThemeProvider } from "@/components/theme-provider"; export const metadata: Metadata = { - title: "blcklst - Modern Fashion Brand", + title: "blcklst - not everyone get blcklsted", description: "not everyone gets blcklsted - discover carefully curated fashion pieces that define modern elegance.", }; @@ -13,16 +14,36 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - <html lang="en"> + <html lang="en" suppressHydrationWarning> <head> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap" rel="stylesheet" /> + {/* Preload both logo variants for instant theme switching */} + <link + rel="preload" + href="/black-logo.png" + as="image" + type="image/png" + /> + <link + rel="preload" + href="/white-logo.png" + as="image" + type="image/png" + /> </head> <body className="font-sans antialiased"> - {children} - <Toaster /> + <ThemeProvider + attribute="class" + defaultTheme="system" + enableSystem + disableTransitionOnChange + > + {children} + <Toaster /> + </ThemeProvider> </body> </html> ); diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 7a9bea6..7d6b7a2 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -93,10 +93,10 @@ export default function Home() { <HeroSection /> {/* Featured Products Section */} - <section className="py-16 bg-white"> + <section className="py-16 bg-white dark:bg-neutral-950"> <div className="container mx-auto px-4"> <div className="text-center mb-12"> - <h2 className="text-3xl font-bold mb-4">Featured Products</h2> + <h2 className="text-3xl font-bold mb-4 text-foreground">Featured Products</h2> <p className="text-muted-foreground max-w-2xl mx-auto"> Discover our handpicked selection of trending items that our customers love most. </p> @@ -117,7 +117,7 @@ export default function Home() { </section> {/* Categories Section */} - <section className="py-16 bg-neutral-50"> + <section className="py-16 bg-neutral-50 dark:bg-neutral-900"> <div className="container mx-auto px-4"> <div className="text-center mb-12"> <h2 className="text-3xl font-bold mb-4">Shop by Category</h2> @@ -130,17 +130,15 @@ export default function Home() { {[ { name: "Women", image: "/api/placeholder/600/400", count: "2,345+ items" }, { name: "Men", image: "/api/placeholder/600/400", count: "1,892+ items" }, - { name: "Kids", image: "/api/placeholder/600/400", count: "756+ items" }, + { name: "Kids", image: "/api/placeholder/600/400", count: "956+ items" }, ].map((category) => ( - <div - key={category.name} - className="relative group cursor-pointer overflow-hidden rounded-lg bg-gradient-to-br from-neutral-200 to-neutral-300 aspect-[4/3]" - > - <div className="absolute inset-0 bg-black/20 group-hover:bg-black/30 transition-colors" /> - <div className="absolute inset-0 flex flex-col justify-end p-6 text-white"> + <div key={category.name} className="group relative overflow-hidden rounded-2xl bg-neutral-100 dark:bg-neutral-800 aspect-[4/3]"> + <div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent" /> + <div className="absolute bottom-6 left-6 text-white"> <h3 className="text-2xl font-bold mb-1">{category.name}</h3> - <p className="text-sm opacity-90">{category.count}</p> + <p className="text-neutral-200">{category.count}</p> </div> + <div className="absolute inset-0 bg-black/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300" /> </div> ))} </div> diff --git a/frontend/src/components/footer.tsx b/frontend/src/components/footer.tsx index d7c8ae7..d64167f 100644 --- a/frontend/src/components/footer.tsx +++ b/frontend/src/components/footer.tsx @@ -1,24 +1,38 @@ +"use client"; + +import { useEffect } from "react"; import Link from "next/link"; import Image from "next/image"; -import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; -import { Separator } from "@/components/ui/separator"; +import { Input } from "@/components/ui/input"; import { Facebook, Instagram, Twitter, Youtube, - Mail, Phone, + Mail, MapPin, - CreditCard, - Shield, Truck, RefreshCw, + Shield, + CreditCard, } from "lucide-react"; export function Footer() { - const currentYear = new Date().getFullYear(); + // Preload both logo variants + useEffect(() => { + const preloadLogos = () => { + if (typeof window !== 'undefined') { + const lightLogo = new window.Image(); + const darkLogo = new window.Image(); + lightLogo.src = '/black-logo.png'; + darkLogo.src = '/white-logo.png'; + } + }; + + preloadLogos(); + }, []); const footerLinks = { shop: [ @@ -53,25 +67,25 @@ export function Footer() { }; return ( - <footer className="bg-neutral-50 border-t"> + <footer className="bg-neutral-50 dark:bg-neutral-900 border-t dark:border-neutral-800"> {/* Newsletter Section */} - <div className="bg-black text-white"> + <div className="bg-black dark:bg-neutral-950 text-white"> <div className="container mx-auto px-4 py-12"> <div className="max-w-2xl mx-auto text-center"> <h2 className="text-2xl font-bold mb-4">Stay in the loop</h2> - <p className="text-neutral-300 mb-6"> + <p className="text-neutral-300 dark:text-neutral-400 mb-6"> Subscribe to our newsletter and be the first to know about new arrivals, sales, and exclusive offers. </p> <div className="flex max-w-md mx-auto gap-3"> <Input placeholder="Enter your email" - className="bg-white text-black placeholder:text-gray-500" + className="bg-white dark:bg-neutral-800 text-black dark:text-white placeholder:text-gray-500 dark:placeholder:text-gray-400 border-neutral-300 dark:border-neutral-600" /> - <Button variant="secondary" className="px-8"> + <Button variant="secondary" className="px-8 bg-white dark:bg-neutral-700 text-black dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-600"> Subscribe </Button> </div> - <p className="text-xs text-neutral-400 mt-3"> + <p className="text-xs text-neutral-400 dark:text-neutral-500 mt-3"> By subscribing, you agree to our Privacy Policy and Terms of Service. </p> </div> @@ -84,12 +98,23 @@ export function Footer() { {/* Brand Info */} <div className="lg:col-span-2"> <Link href="/" className="flex items-center space-x-2 mb-4"> + {/* Light theme logo - visible by default, hidden in dark mode */} <Image src="/black-logo.png" alt="blcklst" width={140} height={45} - className="h-8 w-auto" + className="h-8 w-auto block dark:hidden" + priority + /> + {/* Dark theme logo - hidden by default, visible in dark mode */} + <Image + src="/white-logo.png" + alt="blcklst" + width={140} + height={45} + className="h-8 w-auto hidden dark:block" + priority /> </Link> <p className="text-muted-foreground mb-6 max-w-md"> @@ -98,22 +123,22 @@ export function Footer() { {/* Contact Info */} <div className="space-y-3 mb-6"> - <div className="flex items-center space-x-3 text-sm"> + <div className="flex items-center space-x-3 text-sm text-foreground"> <Phone className="h-4 w-4 text-muted-foreground" /> <span>+1 (555) 123-4567</span> </div> - <div className="flex items-center space-x-3 text-sm"> + <div className="flex items-center space-x-3 text-sm text-foreground"> <Mail className="h-4 w-4 text-muted-foreground" /> <span>[email protected]</span> </div> - <div className="flex items-center space-x-3 text-sm"> + <div className="flex items-center space-x-3 text-sm text-foreground"> <MapPin className="h-4 w-4 text-muted-foreground" /> <span>123 Fashion St, New York, NY 10001</span> </div> </div> {/* Social Media */} - <div className="flex space-x-4"> + <div className="flex space-x-4 mb-4"> <Link href="#" className="text-muted-foreground hover:text-foreground"> <Facebook className="h-5 w-5" /> </Link> @@ -127,6 +152,28 @@ export function Footer() { <Youtube className="h-5 w-5" /> </Link> </div> + + {/* Made with Love */} + <p className="text-xs text-muted-foreground/80 flex items-center"> + made with β€οΈ by{" "} + <a + href="https://mavvisuals.gumroad.com" + target="_blank" + rel="noopener noreferrer" + className="text-primary/80 hover:text-primary transition-colors mx-1 font-normal" + > + maanv + </a> + &{" "} + <a + href="https://surgot.in" + target="_blank" + rel="noopener noreferrer" + className="text-primary/80 hover:text-primary transition-colors ml-1 font-normal" + > + surgot + </a> + </p> </div> {/* Shop Links */} @@ -225,19 +272,20 @@ export function Footer() { </div> {/* Bottom Footer */} - <div className="border-t bg-neutral-100"> + <div className="border-t dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-800"> <div className="container mx-auto px-4 py-6"> - <div className="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0"> - <div className="flex flex-col md:flex-row items-center space-y-2 md:space-y-0 md:space-x-6"> + <div className="flex flex-col lg:flex-row justify-between items-center space-y-4 lg:space-y-0 lg:gap-8"> + {/* Left: Copyright and Legal Links */} + <div className="flex flex-col sm:flex-row items-center space-y-2 sm:space-y-0 sm:space-x-6 text-center sm:text-left"> <p className="text-sm text-muted-foreground"> - Β© {currentYear} blcklst. All rights reserved. + Β© {new Date().getFullYear()} blcklst. All rights reserved. </p> <div className="flex space-x-4"> {footerLinks.legal.map((link) => ( <Link key={link.name} href={link.href} - className="text-xs text-muted-foreground hover:text-foreground" + className="text-xs text-muted-foreground hover:text-foreground transition-colors" > {link.name} </Link> @@ -245,14 +293,14 @@ export function Footer() { </div> </div> - {/* Payment Methods */} + {/* Right: Payment Methods */} <div className="flex items-center space-x-2"> <span className="text-xs text-muted-foreground mr-2">We accept:</span> <div className="flex space-x-1"> {["Visa", "Mastercard", "AmEx", "PayPal", "Apple Pay"].map((method) => ( <div key={method} - className="bg-white border rounded px-2 py-1 text-xs font-medium" + className="bg-white dark:bg-neutral-700 border dark:border-neutral-600 rounded px-2 py-1 text-xs font-medium text-foreground" > {method} </div> diff --git a/frontend/src/components/header.tsx b/frontend/src/components/header.tsx index d64061a..851f637 100644 --- a/frontend/src/components/header.tsx +++ b/frontend/src/components/header.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import Link from "next/link"; import Image from "next/image"; import { Button } from "@/components/ui/button"; @@ -22,7 +22,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { ThemeToggle } from "@/components/theme-toggle"; import { Search, ShoppingBag, @@ -30,7 +30,6 @@ import { User, Menu, Phone, - Mail, Truck, RefreshCw, Shield, @@ -40,7 +39,7 @@ import { export function Header() { const [cartItems] = useState(3); const [wishlistItems] = useState(5); - + const categories = [ { title: "Women", @@ -73,6 +72,20 @@ export function Header() { }, ]; + // Preload both logo variants to ensure smooth loading + useEffect(() => { + const preloadLogos = () => { + if (typeof window !== 'undefined') { + const lightLogo = new window.Image(); + const darkLogo = new window.Image(); + lightLogo.src = '/black-logo.png'; + darkLogo.src = '/white-logo.png'; + } + }; + + preloadLogos(); + }, []); + return ( <header className="sticky top-0 z-50 w-full bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"> {/* Top banner */} @@ -83,20 +96,24 @@ export function Header() { </div> {/* Announcement bar */} - <div className="bg-neutral-100 text-center py-2 px-4"> + <div className="bg-neutral-100 dark:bg-neutral-800 text-center py-2 px-4"> <p className="text-sm"> π₯ <span className="font-semibold">WINTER SALE</span> - Up to 50% off on selected items </p> </div> {/* Main header */} - <div className="border-b"> + <div className="border-b dark:border-neutral-800"> <div className="container mx-auto px-4"> <div className="flex h-16 items-center justify-between"> {/* Mobile menu */} <Sheet> <SheetTrigger asChild> - <Button variant="ghost" size="icon" className="md:hidden"> + <Button + variant="ghost" + size="icon" + className="md:hidden nav-button-transparent backdrop-blur-sm" + > <Menu className="h-5 w-5" /> </Button> </SheetTrigger> @@ -128,12 +145,22 @@ export function Header() { {/* Logo */} <div className="flex items-center"> <Link href="/" className="flex items-center space-x-2"> + {/* Light theme logo - visible by default, hidden in dark mode */} <Image src="/black-logo.png" alt="blcklst" width={120} height={40} - className="h-8 w-auto" + className="h-8 w-auto block dark:hidden" + priority + /> + {/* Dark theme logo - hidden by default, visible in dark mode */} + <Image + src="/white-logo.png" + alt="blcklst" + width={120} + height={40} + className="h-8 w-auto hidden dark:block" priority /> </Link> @@ -144,16 +171,18 @@ export function Header() { <NavigationMenuList> {categories.map((category) => ( <NavigationMenuItem key={category.title}> - <NavigationMenuTrigger className="font-medium"> + <NavigationMenuTrigger + className="font-medium nav-button-transparent backdrop-blur-sm" + > {category.title} </NavigationMenuTrigger> <NavigationMenuContent> - <div className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px]"> + <div className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] nav-dropdown-transparent"> {category.items.map((item) => ( <NavigationMenuLink key={item.name} asChild> <Link href={item.href} - className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground" + className="block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors nav-dropdown-item backdrop-blur-sm" > <div className="text-sm font-medium leading-none">{item.name}</div> </Link> @@ -165,7 +194,7 @@ export function Header() { ))} <NavigationMenuItem> <NavigationMenuLink asChild> - <Link href="/sale" className="font-medium text-red-600 hover:text-red-700"> + <Link href="/sale" className="font-medium text-red-600 hover:text-red-700 px-4 py-2 rounded-md nav-button-transparent backdrop-blur-sm transition-colors"> Sale </Link> </NavigationMenuLink> @@ -187,42 +216,57 @@ export function Header() { {/* Action buttons */} <div className="flex items-center space-x-2"> {/* Search icon for mobile */} - <Button variant="ghost" size="icon" className="lg:hidden"> + <Button + variant="ghost" + size="icon" + className="lg:hidden nav-button-transparent backdrop-blur-sm" + > <Search className="h-5 w-5" /> </Button> + {/* Theme toggle */} + <ThemeToggle /> + {/* User menu */} <DropdownMenu> <DropdownMenuTrigger asChild> - <Button variant="ghost" size="icon"> + <Button + variant="ghost" + size="icon" + className="nav-button-transparent backdrop-blur-sm" + > <User className="h-5 w-5" /> </Button> </DropdownMenuTrigger> - <DropdownMenuContent align="end" className="w-56"> - <DropdownMenuItem> + <DropdownMenuContent align="end" className="w-56 nav-dropdown-transparent"> + <DropdownMenuItem className="nav-dropdown-item"> <User className="mr-2 h-4 w-4" /> My Account </DropdownMenuItem> - <DropdownMenuItem> + <DropdownMenuItem className="nav-dropdown-item"> <Truck className="mr-2 h-4 w-4" /> Orders </DropdownMenuItem> - <DropdownMenuItem> + <DropdownMenuItem className="nav-dropdown-item"> <Heart className="mr-2 h-4 w-4" /> Wishlist </DropdownMenuItem> - <DropdownMenuSeparator /> - <DropdownMenuItem> + <DropdownMenuSeparator className="bg-white/20 dark:bg-neutral-800/50" /> + <DropdownMenuItem className="nav-dropdown-item"> Sign In </DropdownMenuItem> - <DropdownMenuItem> + <DropdownMenuItem className="nav-dropdown-item"> Create Account </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> {/* Wishlist */} - <Button variant="ghost" size="icon" className="relative"> + <Button + variant="ghost" + size="icon" + className="relative nav-button-transparent backdrop-blur-sm" + > <Heart className="h-5 w-5" /> {wishlistItems > 0 && ( <Badge className="absolute -top-1 -right-1 h-5 w-5 rounded-full p-0 text-xs"> @@ -232,7 +276,11 @@ export function Header() { </Button> {/* Cart */} - <Button variant="ghost" size="icon" className="relative"> + <Button + variant="ghost" + size="icon" + className="relative nav-button-transparent backdrop-blur-sm" + > <ShoppingBag className="h-5 w-5" /> {cartItems > 0 && ( <Badge className="absolute -top-1 -right-1 h-5 w-5 rounded-full p-0 text-xs"> @@ -244,14 +292,18 @@ export function Header() { {/* Language/Currency */} <DropdownMenu> <DropdownMenuTrigger asChild> - <Button variant="ghost" size="icon"> + <Button + variant="ghost" + size="icon" + className="nav-button-transparent backdrop-blur-sm" + > <Globe className="h-5 w-5" /> </Button> </DropdownMenuTrigger> - <DropdownMenuContent align="end"> - <DropdownMenuItem>πΊπΈ USD</DropdownMenuItem> - <DropdownMenuItem>πͺπΊ EUR</DropdownMenuItem> - <DropdownMenuItem>π¬π§ GBP</DropdownMenuItem> + <DropdownMenuContent align="end" className="nav-dropdown-transparent"> + <DropdownMenuItem className="nav-dropdown-item">πΊπΈ USD</DropdownMenuItem> + <DropdownMenuItem className="nav-dropdown-item">πͺπΊ EUR</DropdownMenuItem> + <DropdownMenuItem className="nav-dropdown-item">π¬π§ GBP</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </div> @@ -260,7 +312,7 @@ export function Header() { </div> {/* Service features */} - <div className="hidden md:block border-b bg-neutral-50"> + <div className="hidden md:block border-b dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900"> <div className="container mx-auto px-4"> <div className="grid grid-cols-4 gap-4 py-2 text-xs text-muted-foreground"> <div className="flex items-center justify-center space-x-1"> diff --git a/frontend/src/components/hero-section.tsx b/frontend/src/components/hero-section.tsx index 2450365..7806cbe 100644 --- a/frontend/src/components/hero-section.tsx +++ b/frontend/src/components/hero-section.tsx @@ -7,7 +7,7 @@ import Link from "next/link"; export function HeroSection() { return ( - <section className="relative overflow-hidden bg-gradient-to-br from-neutral-50 via-white to-neutral-100"> + <section className="relative overflow-hidden bg-gradient-to-br from-neutral-50 via-white to-neutral-100 dark:from-neutral-950 dark:via-neutral-900 dark:to-neutral-800"> {/* Background decoration */} <div className="absolute inset-0 bg-grid-pattern opacity-5"></div> <div className="absolute top-20 right-20 w-72 h-72 bg-primary/5 rounded-full blur-3xl"></div> @@ -89,10 +89,10 @@ export function HeroSection() { {/* Right side - Product showcase */} <div className="relative"> {/* Main product image */} - <div className="relative bg-gradient-to-br from-neutral-100 to-neutral-200 rounded-2xl p-8 lg:p-12"> - <div className="aspect-[3/4] bg-gradient-to-br from-neutral-300 to-neutral-400 rounded-xl flex items-center justify-center"> - <div className="text-center text-neutral-600"> - <div className="w-16 h-16 bg-neutral-500 rounded-full mx-auto mb-4 flex items-center justify-center"> + <div className="relative bg-gradient-to-br from-neutral-100 to-neutral-200 dark:from-neutral-800 dark:to-neutral-700 rounded-2xl p-8 lg:p-12"> + <div className="aspect-[3/4] bg-gradient-to-br from-neutral-300 to-neutral-400 dark:from-neutral-600 dark:to-neutral-500 rounded-xl flex items-center justify-center"> + <div className="text-center text-neutral-600 dark:text-neutral-300"> + <div className="w-16 h-16 bg-neutral-500 dark:bg-neutral-400 rounded-full mx-auto mb-4 flex items-center justify-center"> <Sparkles className="w-8 h-8 text-white" /> </div> <p className="font-medium">Featured Product</p> @@ -107,9 +107,9 @@ export function HeroSection() { </div> {/* Floating product cards */} - <div className="absolute -left-6 top-1/4 bg-white rounded-lg shadow-lg p-4 max-w-[200px] border"> + <div className="absolute -left-6 top-1/4 bg-white dark:bg-neutral-800 rounded-lg shadow-lg p-4 max-w-[200px] border dark:border-neutral-700"> <div className="flex items-center space-x-3"> - <div className="w-12 h-12 bg-gradient-to-br from-pink-100 to-pink-200 rounded-lg"></div> + <div className="w-12 h-12 bg-gradient-to-br from-pink-100 to-pink-200 dark:from-pink-900 dark:to-pink-800 rounded-lg"></div> <div> <p className="font-medium text-sm">Summer Dress</p> <p className="text-xs text-muted-foreground">$89.99</p> @@ -121,9 +121,9 @@ export function HeroSection() { </div> </div> - <div className="absolute -right-8 bottom-1/4 bg-white rounded-lg shadow-lg p-4 max-w-[200px] border"> + <div className="absolute -right-8 bottom-1/4 bg-white dark:bg-neutral-800 rounded-lg shadow-lg p-4 max-w-[200px] border dark:border-neutral-700"> <div className="flex items-center space-x-3"> - <div className="w-12 h-12 bg-gradient-to-br from-blue-100 to-blue-200 rounded-lg"></div> + <div className="w-12 h-12 bg-gradient-to-br from-blue-100 to-blue-200 dark:from-blue-900 dark:to-blue-800 rounded-lg"></div> <div> <p className="font-medium text-sm">Casual Sneakers</p> <p className="text-xs text-muted-foreground">$129.99</p> @@ -150,7 +150,7 @@ export function HeroSection() { {/* Bottom wave */} <div className="absolute bottom-0 left-0 right-0"> <svg - className="w-full h-12 fill-white" + className="w-full h-12 fill-white dark:fill-neutral-900" viewBox="0 0 1200 120" preserveAspectRatio="none" > diff --git a/frontend/src/components/product-card.tsx b/frontend/src/components/product-card.tsx index f790c98..25717f4 100644 --- a/frontend/src/components/product-card.tsx +++ b/frontend/src/components/product-card.tsx @@ -71,16 +71,16 @@ export function ProductCard({ return ( <Card - className="group relative overflow-hidden border-0 shadow-sm hover:shadow-xl transition-all duration-300 cursor-pointer" + className="group relative overflow-hidden border-0 shadow-sm hover:shadow-xl dark:shadow-black/30 dark:hover:shadow-black/50 transition-all duration-300 cursor-pointer bg-white dark:bg-neutral-900" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > <Link href={`/product/${id}`}> <div className="relative overflow-hidden"> {/* Product Image */} - <div className="aspect-[3/4] bg-neutral-100 relative overflow-hidden"> + <div className="aspect-[3/4] bg-neutral-100 dark:bg-neutral-800 relative overflow-hidden"> <div - className="w-full h-full bg-gradient-to-br from-neutral-200 to-neutral-300 flex items-center justify-center transition-transform duration-300 group-hover:scale-105" + className="w-full h-full bg-gradient-to-br from-neutral-200 to-neutral-300 dark:from-neutral-700 dark:to-neutral-800 flex items-center justify-center transition-transform duration-300 group-hover:scale-105" style={{ backgroundImage: `url(${allImages[currentImage]})`, backgroundSize: 'cover', @@ -88,9 +88,9 @@ export function ProductCard({ }} > {/* Placeholder for actual images */} - <div className="text-center text-neutral-500"> - <div className="w-16 h-16 bg-neutral-400 rounded-full mx-auto mb-2 flex items-center justify-center"> - <ShoppingBag className="w-8 h-8 text-white" /> + <div className="text-center text-neutral-500 dark:text-neutral-400"> + <div className="w-16 h-16 bg-neutral-400 dark:bg-neutral-700 rounded-full mx-auto mb-2 flex items-center justify-center"> + <ShoppingBag className="w-8 h-8 text-white dark:text-neutral-300" /> </div> <p className="text-sm font-medium">{name}</p> </div> @@ -99,12 +99,12 @@ export function ProductCard({ {/* Badges */} <div className="absolute top-3 left-3 flex flex-col gap-2"> {isNew && ( - <Badge className="bg-blue-500 hover:bg-blue-600 text-white"> + <Badge className="bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white shadow-lg"> New </Badge> )} {isSale && ( - <Badge className="bg-red-500 hover:bg-red-600 text-white"> + <Badge className="bg-red-600 hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600 text-white shadow-lg"> -{discountPercentage}% </Badge> )} @@ -114,11 +114,11 @@ export function ProductCard({ <Button variant="ghost" size="icon" - className="absolute top-3 right-3 bg-white/80 hover:bg-white text-gray-600 hover:text-red-500 transition-colors" + className="absolute top-3 right-3 bg-white/90 dark:bg-neutral-900/90 hover:bg-white dark:hover:bg-neutral-900 text-gray-600 dark:text-gray-300 hover:text-red-500 dark:hover:text-red-400 transition-colors shadow-lg dark:shadow-black/20" onClick={handleWishlistToggle} > <Heart - className={`h-4 w-4 ${wishlisted ? 'fill-red-500 text-red-500' : ''}`} + className={`h-4 w-4 ${wishlisted ? 'fill-red-500 text-red-500 dark:fill-red-400 dark:text-red-400' : ''}`} /> </Button> @@ -129,7 +129,7 @@ export function ProductCard({ <button key={index} className={`w-2 h-2 rounded-full transition-colors ${ - index === currentImage ? 'bg-white' : 'bg-white/50' + index === currentImage ? 'bg-white dark:bg-neutral-300' : 'bg-white/50 dark:bg-neutral-500/50' }`} onClick={(e) => { e.preventDefault(); @@ -142,7 +142,7 @@ export function ProductCard({ )} {/* Hover overlay with actions */} - <div className={`absolute inset-0 bg-black/20 flex items-center justify-center transition-opacity duration-300 ${ + <div className={`absolute inset-0 bg-black/30 dark:bg-black/50 flex items-center justify-center transition-opacity duration-300 ${ isHovered ? 'opacity-100' : 'opacity-0' }`}> <div className="flex gap-3"> @@ -150,7 +150,7 @@ export function ProductCard({ variant="secondary" size="sm" onClick={handleQuickView} - className="bg-white/90 hover:bg-white text-gray-900" + className="bg-white/95 dark:bg-neutral-900/95 hover:bg-white dark:hover:bg-neutral-900 text-gray-900 dark:text-white shadow-lg dark:shadow-black/20" > <Eye className="h-4 w-4 mr-2" /> Quick View @@ -158,7 +158,7 @@ export function ProductCard({ <Button size="sm" onClick={handleAddToCart} - className="bg-primary hover:bg-primary/90" + className="bg-primary hover:bg-primary/90 dark:bg-primary dark:hover:bg-primary/90 shadow-lg dark:shadow-black/20" > <ShoppingBag className="h-4 w-4 mr-2" /> Add to Cart @@ -168,14 +168,14 @@ export function ProductCard({ </div> {/* Product Info */} - <CardContent className="p-4 space-y-3"> + <CardContent className="p-4 space-y-3 bg-white dark:bg-neutral-900"> {/* Category */} - <p className="text-xs text-muted-foreground uppercase tracking-wide"> + <p className="text-xs text-muted-foreground dark:text-neutral-500 uppercase tracking-wide"> {category} </p> {/* Product Name */} - <h3 className="font-semibold text-sm leading-tight line-clamp-2 group-hover:text-primary transition-colors"> + <h3 className="font-semibold text-sm leading-tight line-clamp-2 group-hover:text-primary dark:group-hover:text-primary transition-colors text-foreground dark:text-neutral-100"> {name} </h3> @@ -187,13 +187,13 @@ export function ProductCard({ key={star} className={`h-3 w-3 ${ star <= rating - ? 'fill-yellow-400 text-yellow-400' - : 'text-gray-300' + ? 'fill-yellow-400 text-yellow-400 dark:fill-yellow-500 dark:text-yellow-500' + : 'text-gray-300 dark:text-neutral-600' }`} /> ))} </div> - <span className="text-xs text-muted-foreground"> + <span className="text-xs text-muted-foreground dark:text-neutral-500"> ({reviewCount}) </span> </div> @@ -204,12 +204,12 @@ export function ProductCard({ {colors.slice(0, 4).map((color, index) => ( <div key={index} - className="w-4 h-4 rounded-full border border-gray-300" + className="w-4 h-4 rounded-full border border-gray-300 dark:border-neutral-600" style={{ backgroundColor: color }} /> ))} {colors.length > 4 && ( - <span className="text-xs text-muted-foreground ml-1"> + <span className="text-xs text-muted-foreground dark:text-neutral-500 ml-1"> +{colors.length - 4} </span> )} @@ -218,11 +218,11 @@ export function ProductCard({ {/* Price */} <div className="flex items-center gap-2"> - <span className="font-bold text-lg"> + <span className="font-bold text-lg text-foreground dark:text-neutral-100"> ${price.toFixed(2)} </span> {originalPrice && ( - <span className="text-sm text-muted-foreground line-through"> + <span className="text-sm text-muted-foreground dark:text-neutral-500 line-through"> ${originalPrice.toFixed(2)} </span> )} @@ -232,12 +232,12 @@ export function ProductCard({ {sizes.length > 0 && ( <div className="flex flex-wrap gap-1"> {sizes.slice(0, 4).map((size) => ( - <Badge key={size} variant="outline" className="text-xs px-2 py-1"> + <Badge key={size} variant="outline" className="text-xs px-2 py-1 border-neutral-300 dark:border-neutral-600 text-neutral-700 dark:text-neutral-300"> {size} </Badge> ))} {sizes.length > 4 && ( - <Badge variant="outline" className="text-xs px-2 py-1"> + <Badge variant="outline" className="text-xs px-2 py-1 border-neutral-300 dark:border-neutral-600 text-neutral-700 dark:text-neutral-300"> +{sizes.length - 4} </Badge> )} diff --git a/frontend/src/components/theme-provider.tsx b/frontend/src/components/theme-provider.tsx new file mode 100644 index 0000000..220a1f8 --- /dev/null +++ b/frontend/src/components/theme-provider.tsx @@ -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> +}
\ No newline at end of file diff --git a/frontend/src/components/theme-toggle.tsx b/frontend/src/components/theme-toggle.tsx new file mode 100644 index 0000000..17df44e --- /dev/null +++ b/frontend/src/components/theme-toggle.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import { Moon, Sun, Monitor } from "lucide-react" +import { useTheme } from "next-themes" + +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +export function ThemeToggle() { + const { setTheme, theme } = useTheme() + + return ( + <DropdownMenu> + <DropdownMenuTrigger asChild> + <Button + variant="ghost" + size="icon" + className="nav-button-transparent backdrop-blur-sm" + > + <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> + </DropdownMenuTrigger> + <DropdownMenuContent align="end" className="nav-dropdown-transparent"> + <DropdownMenuItem onClick={() => setTheme("light")} className="flex items-center gap-2 nav-dropdown-item"> + <Sun className="h-4 w-4" /> + Light + {theme === "light" && <span className="ml-auto text-xs">β</span>} + </DropdownMenuItem> + <DropdownMenuItem onClick={() => setTheme("dark")} className="flex items-center gap-2 nav-dropdown-item"> + <Moon className="h-4 w-4" /> + Dark + {theme === "dark" && <span className="ml-auto text-xs">β</span>} + </DropdownMenuItem> + <DropdownMenuItem onClick={() => setTheme("system")} className="flex items-center gap-2 nav-dropdown-item"> + <Monitor className="h-4 w-4" /> + System + {theme === "system" && <span className="ml-auto text-xs">β</span>} + </DropdownMenuItem> + </DropdownMenuContent> + </DropdownMenu> + ) +}
\ No newline at end of file |