From b28259ae6ead09f7cce3a40fe9464d8a7b2402ca Mon Sep 17 00:00:00 2001 From: gneo0 Date: Sun, 2 Mar 2025 22:06:26 +0200 Subject: [PATCH] Add responsive navbar and toggle mode functionality --- package-lock.json | 10 ++++++++ package.json | 1 + src/App.jsx | 15 +++-------- src/components/Header.jsx | 21 ++++++++++++++++ src/components/Navbar.jsx | 49 ++++++++++++++++++++++++++++++++++++ src/context/ThemeContext.jsx | 48 +++++++++++++++++++++++++++++++++++ src/index.css | 2 ++ src/lib/constants.js | 8 ++++++ src/main.jsx | 19 ++++++++------ 9 files changed, 153 insertions(+), 20 deletions(-) create mode 100644 src/components/Header.jsx create mode 100644 src/components/Navbar.jsx create mode 100644 src/context/ThemeContext.jsx create mode 100644 src/lib/constants.js diff --git a/package-lock.json b/package-lock.json index 50e1cd5..a8d5dd0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "motion": "^12.4.7", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-icons": "^5.5.0", "tailwind-merge": "^3.0.1", "tailwindcss-animate": "^1.0.7" }, @@ -6386,6 +6387,15 @@ "react": "^19.0.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 175ced4..244bd51 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "motion": "^12.4.7", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-icons": "^5.5.0", "tailwind-merge": "^3.0.1", "tailwindcss-animate": "^1.0.7" }, diff --git a/src/App.jsx b/src/App.jsx index 5329c17..33570b4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,6 +10,7 @@ import LoanDetails from "./components/LoanDetails"; import Result from "./components/Result"; import { auth } from "./firebaseConfig"; // Import Firebase Auth import LazyBackground from "./components/LazyLoadBackground"; +import Header from "./components/Header"; function App() { const [selectedGoal, setSelectedGoal] = useState(""); const [selectedPropertyType, setSelectedPropertyType] = useState(""); @@ -70,20 +71,10 @@ function App() { return ( -
- Real Estate Logo -

- {" "} - Real Estate Calculator{" "} -

-
+
- + +
+ Real Estate Logo +

+ Real Estate Calculator +

+
+ +
+ ); +} + +export default Header; diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx new file mode 100644 index 0000000..74edf9a --- /dev/null +++ b/src/components/Navbar.jsx @@ -0,0 +1,49 @@ +import { useState } from "react"; +import { + RiMenu3Line, + RiCloseLargeFill, + RiSunLine, + RiMoonClearLine, +} from "react-icons/ri"; + +import { NAV_LINKS } from "../lib/constants"; +import { useTheme } from "../context/ThemeContext"; + +function Navbar() { + const { isDarkMode, handleToggleTheme } = useTheme(); + const [isMobileNavOpen, setIsMobileNavOpen] = useState(false); + + return ( +
+ + + +
+ ); +} + +export default Navbar; diff --git a/src/context/ThemeContext.jsx b/src/context/ThemeContext.jsx new file mode 100644 index 0000000..0cb297b --- /dev/null +++ b/src/context/ThemeContext.jsx @@ -0,0 +1,48 @@ +import { createContext, useContext, useEffect, useState } from "react"; + +const ThemeContext = createContext(); + +export default function ThemeProvider({ children }) { + const [isDarkMode, setIsDarkMode] = useState(() => { + const storedTheme = localStorage.getItem("theme"); + return storedTheme === "dark"; + }); + + useEffect(() => { + if (isDarkMode) { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } + }, []); + + const handleToggleTheme = () => { + if (!isDarkMode) { + document.documentElement.classList.add("dark"); + setIsDarkMode(true); + localStorage.setItem("theme", "dark"); + } else { + document.documentElement.classList.remove("dark"); + setIsDarkMode(false); + localStorage.setItem("theme", "light"); + } + }; + + return ( + + {children} + + ); +} + +export const useTheme = () => { + const context = useContext(ThemeContext); + + if (context === undefined) { + throw new Error("ThemeProvider must be used inside it's context"); + } + + return context; +}; diff --git a/src/index.css b/src/index.css index f1d8c73..0229963 100644 --- a/src/index.css +++ b/src/index.css @@ -1 +1,3 @@ @import "tailwindcss"; + +@custom-variant dark (&:where(.dark, .dark *)); \ No newline at end of file diff --git a/src/lib/constants.js b/src/lib/constants.js new file mode 100644 index 0000000..47cd7e8 --- /dev/null +++ b/src/lib/constants.js @@ -0,0 +1,8 @@ +export const NAV_LINKS = [ + { href: "/", label: "Home" }, + { href: "/docs", label: "Docs" }, + { href: "/features", label: "Features" }, + { href: "/api-ref", label: "API" }, + { href: "/community", label: "Community" }, + { href: "/contact", label: "Contact" }, +]; diff --git a/src/main.jsx b/src/main.jsx index b9a1a6d..980bfbd 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,10 +1,13 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.jsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.jsx"; +import ThemeProvider from "./context/ThemeContext.jsx"; -createRoot(document.getElementById('root')).render( +createRoot(document.getElementById("root")).render( - - , -) + + + + +);