Implemented a fully responsive dark/light mode toggle switch in the navigation bar with smooth transitions across all components.

This commit is contained in:
wameup
2026-03-10 03:16:12 +00:00
parent 2c4fc7f933
commit 52bcb94d03
7 changed files with 171 additions and 82 deletions

View File

@@ -1,4 +1,5 @@
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { ThemeProvider } from './context/ThemeContext';
import Navbar from './components/layout/Navbar';
import Footer from './components/layout/Footer';
import Home from './pages/Home';
@@ -14,26 +15,28 @@ import NotFound from './pages/NotFound';
function App() {
return (
<Router>
<div className="min-h-screen flex flex-col">
<Navbar />
<main className="flex-grow">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/properties" element={<Properties />} />
<Route path="/properties/:id" element={<PropertyDetail />} />
<Route path="/property-3d" element={<Property3D />} />
<Route path="/about" element={<About />} />
<Route path="/privacy" element={<Privacy />} />
<Route path="/faq" element={<FAQ />} />
<Route path="/blog" element={<Blog />} />
<Route path="/blog/:slug" element={<BlogPost />} />
<Route path = '*' element={<NotFound/>} />
</Routes>
</main>
<Footer />
</div>
</Router>
<ThemeProvider>
<Router>
<div className="min-h-screen flex flex-col">
<Navbar />
<main className="flex-grow">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/properties" element={<Properties />} />
<Route path="/properties/:id" element={<PropertyDetail />} />
<Route path="/property-3d" element={<Property3D />} />
<Route path="/about" element={<About />} />
<Route path="/privacy" element={<Privacy />} />
<Route path="/faq" element={<FAQ />} />
<Route path="/blog" element={<Blog />} />
<Route path="/blog/:slug" element={<BlogPost />} />
<Route path = '*' element={<NotFound/>} />
</Routes>
</main>
<Footer />
</div>
</Router>
</ThemeProvider>
);
}

View File

@@ -4,13 +4,13 @@ import { FaDiscord } from 'react-icons/fa';
function Footer() {
return (
<footer className="bg-secondary-900 text-white">
<footer className="bg-secondary-900 dark:bg-secondary-950 text-white transition-colors duration-300">
<div className="container py-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
{/* Company Info */}
<div>
<h3 className="text-lg font-semibold mb-4">RoyalCity</h3>
<p className="text-secondary-300 text-sm">
<p className="text-secondary-300 dark:text-secondary-400 text-sm transition-colors duration-300">
Your trusted partner in finding the perfect property. We make real estate simple and accessible for everyone.
</p>
</div>
@@ -20,22 +20,22 @@ function Footer() {
<h3 className="text-lg font-semibold mb-4">Quick Links</h3>
<ul className="space-y-2">
<li>
<Link to="/properties" className="text-secondary-300 hover:text-white text-sm">
<Link to="/properties" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 text-sm transition-colors duration-300">
Properties
</Link>
</li>
<li>
<Link to="/about" className="text-secondary-300 hover:text-white text-sm">
<Link to="/about" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 text-sm transition-colors duration-300">
About Us
</Link>
</li>
<li>
<Link to="/faq" className="text-secondary-300 hover:text-white text-sm">
<Link to="/faq" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 text-sm transition-colors duration-300">
FAQ
</Link>
</li>
<li>
<Link to="/privacy" className="text-secondary-300 hover:text-white text-sm">
<Link to="/privacy" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 text-sm transition-colors duration-300">
Privacy Policy
</Link>
</li>
@@ -46,15 +46,15 @@ function Footer() {
<div>
<h3 className="text-lg font-semibold mb-4">Contact</h3>
<ul className="space-y-2">
<li className="flex items-center text-secondary-300 text-sm">
<li className="flex items-center text-secondary-300 dark:text-secondary-400 text-sm transition-colors duration-300">
<FiPhone className="mr-2" />
<span>+1 (555) 123-4567</span>
</li>
<li className="flex items-center text-secondary-300 text-sm">
<li className="flex items-center text-secondary-300 dark:text-secondary-400 text-sm transition-colors duration-300">
<FiMail className="mr-2" />
<span>contact@RoyalCity.com</span>
</li>
<li className="flex items-center text-secondary-300 text-sm">
<li className="flex items-center text-secondary-300 dark:text-secondary-400 text-sm transition-colors duration-300">
<FiMapPin className="mr-2" />
<span>123 Property Street, Real City, RC 12345</span>
</li>
@@ -65,23 +65,23 @@ function Footer() {
<div>
<h3 className="text-lg font-semibold mb-4">Connect With Us</h3>
<div className="flex space-x-4">
<Link to="#" className="text-secondary-300 hover:text-white">
<Link to="#" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 transition-colors duration-300">
<FiTwitter size={20} />
</Link>
<Link to="#" className="text-secondary-300 hover:text-white">
<Link to="#" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 transition-colors duration-300">
<FiLinkedin size={20} />
</Link>
<Link to="#" className="text-secondary-300 hover:text-white">
<Link to="#" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 transition-colors duration-300">
<FiGithub size={20} />
</Link>
<Link to="#" className="text-secondary-300 hover:text-white">
<Link to="#" className="text-secondary-300 dark:text-secondary-400 hover:text-white dark:hover:text-secondary-200 transition-colors duration-300">
<FaDiscord size={20} />
</Link>
</div>
</div>
</div>
<div className="border-t border-secondary-700 mt-8 pt-8 text-center text-secondary-300 text-sm">
<div className="border-t border-secondary-700 dark:border-secondary-800 mt-8 pt-8 text-center text-secondary-300 dark:text-secondary-400 text-sm transition-colors duration-300">
<p>&copy; {new Date().getFullYear()} RoyalCity. All rights reserved.</p>
</div>
</div>

View File

@@ -1,9 +1,11 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { FiMenu, FiX } from 'react-icons/fi';
import { FiMenu, FiX, FiSun, FiMoon } from 'react-icons/fi';
import { useTheme } from '../../context/ThemeContext';
function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const { isDark, toggleTheme } = useTheme();
const navigation = [
{ name: 'Home', href: '/' },
@@ -14,9 +16,9 @@ function Navbar() {
];
return (
<nav className="bg-white shadow-sm">
<nav className="bg-white dark:bg-secondary-900 shadow-sm dark:shadow-md transition-colors duration-300">
<div className="container">
<div className="flex justify-between h-16">
<div className="flex justify-between items-center h-16">
<div className="flex">
<Link to="/" className="flex items-center">
<svg width="30" height="35" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -33,11 +35,22 @@ function Navbar() {
<Link
key={item.name}
to={item.href}
className="text-secondary-600 hover:text-primary-600 px-3 py-2 text-sm font-medium"
className="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 text-sm font-medium transition-colors duration-300"
>
{item.name}
</Link>
))}
{/* Theme Toggle Button */}
<button
onClick={toggleTheme}
className="theme-toggle"
aria-label="Toggle theme"
title={isDark ? 'Light mode' : 'Dark mode'}
>
{isDark ? <FiSun size={20} /> : <FiMoon size={20} />}
</button>
<button
className="btn"
>
@@ -45,11 +58,22 @@ function Navbar() {
</button>
</div>
{/* Mobile menu button */}
<div className="flex items-center md:hidden">
{/* Mobile menu and theme toggle */}
<div className="flex items-center md:hidden space-x-3">
{/* Mobile Theme Toggle Button */}
<button
onClick={toggleTheme}
className="theme-toggle"
aria-label="Toggle theme"
title={isDark ? 'Light mode' : 'Dark mode'}
>
{isDark ? <FiSun size={20} /> : <FiMoon size={20} />}
</button>
{/* Mobile menu button */}
<button
type="button"
className="text-secondary-600 hover:text-primary-600"
className="text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors duration-300"
onClick={() => setIsOpen(!isOpen)}
>
{isOpen ? <FiX size={24} /> : <FiMenu size={24} />}
@@ -59,20 +83,20 @@ function Navbar() {
{/* Mobile Navigation */}
{isOpen && (
<div className="md:hidden">
<div className="md:hidden bg-white dark:bg-secondary-800 transition-colors duration-300">
<div className="pt-2 pb-3 space-y-1">
{navigation.map((item) => (
<Link
key={item.name}
to={item.href}
className="block px-3 py-2 text-base font-medium text-secondary-600 hover:text-primary-600 hover:bg-primary-50"
className="block px-3 py-2 text-base font-medium text-secondary-600 dark:text-secondary-400 hover:text-primary-600 dark:hover:text-primary-400 hover:bg-primary-50 dark:hover:bg-secondary-700 transition-colors duration-300"
onClick={() => setIsOpen(false)}
>
{item.name}
</Link>
))}
<button
className="block px-3 py-2 text-base font-medium text-white bg-primary-600 hover:bg-primary-700"
className="block w-full text-left px-3 py-2 text-base font-medium text-white bg-primary-600 hover:bg-primary-700 transition-colors duration-300"
onClick={() => setIsOpen(false)}
>
Connect

View File

@@ -0,0 +1,50 @@
import { createContext, useContext, useState, useEffect } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [isDark, setIsDark] = useState(false);
// Initialize theme from localStorage on mount
useEffect(() => {
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme) {
setIsDark(savedTheme === 'dark');
} else {
setIsDark(prefersDark);
}
}, []);
// Update DOM and localStorage when theme changes
useEffect(() => {
const htmlElement = document.documentElement;
if (isDark) {
htmlElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
} else {
htmlElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
}, [isDark]);
const toggleTheme = () => {
setIsDark(!isDark);
};
return (
<ThemeContext.Provider value={{ isDark, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}

View File

@@ -10,7 +10,12 @@
}
body {
@apply font-sans text-secondary-800 bg-secondary-50;
@apply font-sans text-secondary-800 bg-secondary-50 dark:text-secondary-100 dark:bg-secondary-950 transition-colors duration-300;
}
/* Smooth transitions for dark mode */
* {
@apply transition-colors duration-300;
}
}
@@ -20,18 +25,23 @@
}
.btn {
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500;
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors duration-300;
}
.btn-secondary {
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-primary-700 bg-primary-100 hover:bg-primary-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500;
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-primary-700 bg-primary-100 hover:bg-primary-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors duration-300 dark:text-primary-400 dark:bg-primary-900 dark:hover:bg-primary-800;
}
.input {
@apply block w-full rounded-md border-secondary-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm;
@apply block w-full rounded-md border-secondary-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm dark:bg-secondary-800 dark:border-secondary-700 dark:text-secondary-100 transition-colors duration-300;
}
.card {
@apply bg-white rounded-lg shadow-md overflow-hidden;
@apply bg-white rounded-lg shadow-md overflow-hidden dark:bg-secondary-800 dark:shadow-lg transition-colors duration-300;
}
/* Theme toggle button */
.theme-toggle {
@apply relative inline-flex items-center justify-center w-10 h-10 rounded-lg text-secondary-600 hover:text-primary-600 hover:bg-secondary-100 dark:text-secondary-400 dark:hover:text-primary-400 dark:hover:bg-secondary-800 transition-colors duration-300;
}
}

View File

@@ -247,8 +247,8 @@ function Home() {
{/* Investment Steps */}
<section className="container">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold mb-4">Start Investing in Minutes</h2>
<p className="text-secondary-600">Your journey to crypto-powered real estate investment</p>
<h2 className="text-3xl font-bold mb-4 dark:text-white">Start Investing in Minutes</h2>
<p className="text-secondary-600 dark:text-secondary-400">Your journey to crypto-powered real estate investment</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
@@ -261,13 +261,13 @@ function Home() {
viewport={{ once: true }}
transition={{ delay: index * 0.2 }}
>
<div className="bg-white p-6 rounded-lg shadow-md text-center">
<div className="bg-primary-50 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<step.icon className="text-2xl text-primary-600" />
<div className="bg-white dark:bg-secondary-900 p-6 rounded-lg shadow-md dark:shadow-lg text-center transition-colors duration-300">
<div className="bg-primary-50 dark:bg-primary-900 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4 transition-colors duration-300">
<step.icon className="text-2xl text-primary-600 dark:text-primary-400" />
</div>
<div className="text-primary-600 text-2xl font-bold mb-4">Step {index + 1}</div>
<h3 className="text-xl font-semibold mb-2">{step.title}</h3>
<p className="text-secondary-600">{step.description}</p>
<div className="text-primary-600 dark:text-primary-400 text-2xl font-bold mb-4">Step {index + 1}</div>
<h3 className="text-xl font-semibold mb-2 dark:text-white">{step.title}</h3>
<p className="text-secondary-600 dark:text-secondary-400">{step.description}</p>
</div>
</motion.div>
))}
@@ -375,26 +375,26 @@ function Home() {
</section>
{/* Why Choose Us */}
<section className="bg-secondary-50 pt-16">
<section className="bg-secondary-50 dark:bg-secondary-800 pt-16 transition-colors duration-300">
<div className="container">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold mb-4">Why Choose RoyalCity</h2>
<p className="text-secondary-600">Experience the future of real estate investment</p>
<h2 className="text-3xl font-bold mb-4 dark:text-white">Why Choose RoyalCity</h2>
<p className="text-secondary-600 dark:text-secondary-400">Experience the future of real estate investment</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{advantages.map((advantage, index) => (
<motion.div
key={index}
className="bg-white p-6 rounded-lg shadow-md text-center"
className="bg-white dark:bg-secondary-900 p-6 rounded-lg shadow-md dark:shadow-lg text-center transition-colors duration-300"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.2 }}
>
<advantage.icon className="text-4xl text-primary-600 mx-auto mb-4" />
<h3 className="text-xl font-semibold mb-2">{advantage.title}</h3>
<p className="text-secondary-600">{advantage.description}</p>
<h3 className="text-xl font-semibold mb-2 dark:text-white">{advantage.title}</h3>
<p className="text-secondary-600 dark:text-secondary-400">{advantage.description}</p>
</motion.div>
))}
</div>
@@ -426,14 +426,14 @@ function Home() {
</section>
{/* Blog */}
<div className="container bg-white py-24">
<div className="container bg-white dark:bg-secondary-800 py-24 transition-colors duration-300">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center max-w-3xl mx-auto mb-12"
>
<h1 className="text-3xl font-bold mb-4">Latest Insights</h1>
<p className="text-secondary-600">
<h1 className="text-3xl font-bold mb-4 dark:text-white">Latest Insights</h1>
<p className="text-secondary-600 dark:text-secondary-400">
Stay informed with our latest articles and market analysis
</p>
</motion.div>
@@ -447,7 +447,7 @@ function Home() {
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 }}
className="bg-white rounded-lg shadow-md overflow-hidden"
className="bg-white dark:bg-secondary-900 rounded-lg shadow-md dark:shadow-lg overflow-hidden transition-colors duration-300"
>
<Link to={`/blog/${post.slug}`}>
<div className="relative h-48">
@@ -461,13 +461,13 @@ function Home() {
</div>
</div>
<div className="p-6">
<h2 className="text-xl font-semibold mb-3 hover:text-primary-600 transition-colors">
<h2 className="text-xl font-semibold mb-3 dark:text-white hover:text-primary-600 transition-colors">
{post.title}
</h2>
<p className="text-secondary-600 mb-4">
<p className="text-secondary-600 dark:text-secondary-400 mb-4">
{post.excerpt}
</p>
<div className="flex items-center text-sm text-secondary-500">
<div className="flex items-center text-sm text-secondary-500 dark:text-secondary-500">
<FiUser className="mr-2" />
<span className="mr-4">{post.author}</span>
<FiClock className="mr-2" />
@@ -488,17 +488,17 @@ function Home() {
className="max-w-3xl mx-auto"
>
<div className="text-center mb-12">
<h2 className="text-3xl font-bold mb-4">Frequently Asked Questions</h2>
<p className="text-secondary-600">Find answers to common questions about our platform, cryptocurrency payments, and real estate investment.</p>
<h2 className="text-3xl font-bold mb-4 dark:text-white">Frequently Asked Questions</h2>
<p className="text-secondary-600 dark:text-secondary-400">Find answers to common questions about our platform, cryptocurrency payments, and real estate investment.</p>
</div>
<div className="space-y-8">
{faqSections.map((section, sectionIndex) => (
<div key={sectionIndex} className="bg-white rounded-lg shadow-md overflow-hidden">
<div className="divide-y divide-secondary-100">
<div key={sectionIndex} className="bg-white dark:bg-secondary-800 rounded-lg shadow-md dark:shadow-lg overflow-hidden transition-colors duration-300">
<div className="divide-y divide-secondary-100 dark:divide-secondary-700">
{section.questions.map((item, questionIndex) => (
<div key={questionIndex} className="p-6">
<button
className="w-full flex justify-between items-center text-left"
className="w-full flex justify-between items-center text-left dark:text-white"
onClick={() => toggleSection(section.title, questionIndex)}
>
<span className="font-medium">{item.question}</span>
@@ -517,7 +517,7 @@ function Home() {
transition={{ duration: 0.3 }}
className="overflow-hidden"
>
<p className="mt-4 text-secondary-600">
<p className="mt-4 text-secondary-600 dark:text-secondary-400">
{item.answer}
</p>
</motion.div>
@@ -583,24 +583,24 @@ function Home() {
</div>
<div className="mt-12 lg:mt-0 flex justify-center">
<div className="bg-white rounded-lg shadow-xl p-8 max-w-sm w-full">
<h3 className="text-2xl font-bold text-gray-900 text-center mb-6">
<div className="bg-white dark:bg-secondary-800 rounded-lg shadow-xl dark:shadow-2xl p-8 max-w-sm w-full transition-colors duration-300">
<h3 className="text-2xl font-bold text-gray-900 dark:text-white text-center mb-6">
Join Discord
</h3>
<p className="text-gray-500 text-center mb-8">
<p className="text-gray-500 dark:text-secondary-400 text-center mb-8 transition-colors duration-300">
Get instant access to our community and start connecting with other investors
</p>
<a
href="https://discord.gg/RoyalCity"
target="_blank"
rel="noopener noreferrer"
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 md:py-4 md:text-lg md:px-10"
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-700 dark:hover:bg-indigo-600 md:py-4 md:text-lg md:px-10 transition-colors duration-300"
>
Join Now
</a>
<p className="mt-4 text-sm text-gray-500 text-center">
<p className="mt-4 text-sm text-gray-500 dark:text-secondary-400 text-center transition-colors duration-300">
Already a member?{' '}
<a href="https://discord.gg/RoyalCity" className="text-indigo-600 hover:text-indigo-500">
<a href="https://discord.gg/RoyalCity" className="text-indigo-600 dark:text-indigo-400 hover:text-indigo-500 dark:hover:text-indigo-300 transition-colors duration-300">
Sign in
</a>
</p>

View File

@@ -3,6 +3,7 @@ export default {
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}"
],
darkMode: 'class',
theme: {
extend: {
colors: {
@@ -30,6 +31,7 @@ export default {
700: '#334155',
800: '#1e293b',
900: '#0f172a',
950: '#020617',
},
},
fontFamily: {