diff --git a/src/App.jsx b/src/App.jsx index 7473e26..2f5b59c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,12 +8,14 @@ import { Heading, HoverCard, HStack, + IconButton, Image, Input, SimpleGrid, Stack, Text, Textarea, + Theme, } from "@chakra-ui/react"; import { Activity, @@ -26,12 +28,14 @@ import { Linkedin, Mail, Menu, + Moon, RadioTower, ShieldCheck, + Sun, TerminalSquare, X, } from "lucide-react"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import resumePdf from "../assets/curriculum.pdf"; @@ -42,6 +46,50 @@ const navItems = ["about", "experience", "skills", "projects", "contact"]; const projectIcons = [ShieldCheck, Cloud, TerminalSquare]; const experienceIcons = [Briefcase, Headphones, Activity]; +function createAppearancePalette(basePalette, isDark) { + if (!isDark) { + return { + ...basePalette, + appBg: "#f8fafc", + bodyText: "#43516a", + cardShadow: "0 16px 40px rgba(31, 41, 55, 0.08)", + elevatedBg: "#ffffff", + footerText: "#64748b", + headerBg: "rgba(255, 255, 255, 0.86)", + hoverCardBg: "#ffffff", + iconPanelShadow: "0 10px 22px rgba(31, 41, 55, 0.12)", + inputBg: "rgba(255, 255, 255, 0.9)", + locationText: "#64748b", + sectionNote: "#64748b", + }; + } + + return { + ...basePalette, + appBg: "#08111e", + bodyText: "#b6c3d9", + cardGradient: + "linear-gradient(145deg, rgba(7, 18, 33, 0.94), rgba(13, 25, 43, 0.96) 46%, rgba(255, 255, 255, 0.04))", + cardShadow: + "0 18px 46px rgba(2, 6, 23, 0.52), 0 10px 26px rgba(15, 23, 42, 0.36)", + elevatedBg: "rgba(10, 19, 34, 0.92)", + footerText: "#8ea2c5", + headerBg: "rgba(6, 14, 26, 0.82)", + heroGradient: + "linear-gradient(135deg, rgba(7, 18, 33, 0.98), rgba(17, 31, 53, 0.96) 52%, rgba(255, 255, 255, 0.04))", + hoverCardBg: "rgba(8, 18, 32, 0.98)", + iconPanelShadow: + "0 12px 28px rgba(2, 6, 23, 0.44), 0 0 0 1px rgba(255, 255, 255, 0.04)", + inputBg: "rgba(7, 16, 30, 0.9)", + locationText: "#8ea2c5", + muted: "rgba(255, 255, 255, 0.08)", + sectionNote: "#8ea2c5", + surface: "#0b1627", + text: "#edf4ff", + border: "rgba(152, 177, 214, 0.22)", + }; +} + function SectionHeading({ eyebrow, title, palette }) { return ( @@ -67,10 +115,10 @@ function ProjectActionHoverCard({ children, label, note, palette, url }) { {children} {note ? ( - + {note} ) : null} @@ -98,8 +146,19 @@ function ProjectActionHoverCard({ children, label, note, palette, url }) { function App() { const { i18n, t } = useTranslation(); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const [appearance, setAppearance] = useState(() => { + if (typeof window === "undefined") return "light"; + + const storedAppearance = window.localStorage.getItem("portfolioAppearance"); + if (storedAppearance === "light" || storedAppearance === "dark") return storedAppearance; + + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; + }); const currentLanguage = i18n.language?.slice(0, 2) || "en"; - const palette = languageThemes[currentLanguage] || languageThemes.en; + const palette = createAppearancePalette( + languageThemes[currentLanguage] || languageThemes.en, + appearance === "dark", + ); const nav = t("nav", { returnObjects: true }); const skillGroups = t("skills.groups", { returnObjects: true }); const experienceItems = t("experience.items", { returnObjects: true }); @@ -134,6 +193,22 @@ function App() { transform: "translateY(0)", }, }; + const formFieldStyles = { + bg: palette.inputBg, + borderColor: palette.border, + color: palette.text, + _placeholder: { + color: palette.footerText, + }, + _focusVisible: { + borderColor: palette.primary, + boxShadow: `0 0 0 1px ${palette.primary}`, + }, + }; + + useEffect(() => { + window.localStorage.setItem("portfolioAppearance", appearance); + }, [appearance]); const changeLanguage = (languageCode) => { window.localStorage.setItem("portfolioLanguage", languageCode); @@ -141,8 +216,15 @@ function App() { setIsMobileMenuOpen(false); }; + const toggleAppearance = () => { + setAppearance((currentAppearance) => + currentAppearance === "dark" ? "light" : "dark", + ); + }; + return ( - + +