From 3cda9893907a2b69506890060b3315d69d785666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20S=C3=B6derberg?= Date: Wed, 11 Mar 2026 23:27:19 +0100 Subject: [PATCH] Light mode --- app/components/BlurredCode.tsx | 32 ++++-- app/components/MatrixRain.tsx | 42 +++++--- app/components/Navbar.tsx | 50 +++++++--- app/components/PhotoGallery.tsx | 13 ++- app/components/ThemeProvider.tsx | 14 +++ app/contact/page.tsx | 40 ++++---- app/gallery/page.tsx | 10 +- app/globals.css | 2 + app/layout.tsx | 40 ++++---- app/page.tsx | 137 ++++++++++---------------- app/projects/code/page.tsx | 83 ++++++++-------- app/projects/design/page.tsx | 34 ++++--- app/projects/page.tsx | 22 ++--- package-lock.json | 11 +++ package.json | 1 + public/assets/logos/naturvardarna.svg | 51 ++++++++++ 16 files changed, 345 insertions(+), 237 deletions(-) create mode 100644 app/components/ThemeProvider.tsx create mode 100644 public/assets/logos/naturvardarna.svg diff --git a/app/components/BlurredCode.tsx b/app/components/BlurredCode.tsx index fd825f9..620927f 100644 --- a/app/components/BlurredCode.tsx +++ b/app/components/BlurredCode.tsx @@ -2,8 +2,10 @@ 'use client'; +import { useState, useEffect } from 'react'; +import { useTheme } from 'next-themes'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { oneDark, oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; const pythonCode = `import quantum_entanglement import neural_net_vibes @@ -44,16 +46,26 @@ if __name__ == "__main__": analyzer.check_is_sandwich(target="glizzy")`; export default function BlurredCode() { + const { resolvedTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => setMounted(true), 0); + return () => clearTimeout(timer); + }, []); + return ( -
-
- - {pythonCode + '\n\n\n' + pythonCode + '\n\n\n' + pythonCode} - +
+
+ {mounted && ( + + {pythonCode + '\n\n\n' + pythonCode + '\n\n\n' + pythonCode} + + )}
); diff --git a/app/components/MatrixRain.tsx b/app/components/MatrixRain.tsx index 9eaaefc..2db9e1a 100644 --- a/app/components/MatrixRain.tsx +++ b/app/components/MatrixRain.tsx @@ -2,23 +2,41 @@ 'use client'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { useTheme } from 'next-themes'; interface MatrixRainProps { color?: string; fps?: number; } -export default function MatrixRain({ color = '#84c0a0', fps = 15 }: MatrixRainProps) { +export default function MatrixRain({ color, fps = 15 }: MatrixRainProps) { const canvasRef = useRef(null); const lastWidth = useRef(0); + const { resolvedTheme } = useTheme(); + const [mounted, setMounted] = useState(false); useEffect(() => { + const timer = setTimeout(() => setMounted(true), 0); + return () => clearTimeout(timer); + }, []); + + useEffect(() => { + if (!mounted) return; + const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); if (!ctx) return; + const isLight = resolvedTheme === 'light'; + // Use slate-50 (#f8fafc) for light mode, #0d1117 for dark mode + const bgColor = isLight ? '#f8fafc' : '#0d1117'; + // The semi-transparent overlay that creates the fading trail effect + const fadeColor = isLight ? 'rgba(248, 250, 252, 0.2)' : 'rgba(13, 17, 23, 0.2)'; + // A darker green for light mode to maintain contrast, or the default/prop color for dark mode + const textColor = color || (isLight ? '#1c2d25' : '#84c0a0'); + const fontSize = 16; let columns = 0; let drops: number[] = []; @@ -26,7 +44,7 @@ export default function MatrixRain({ color = '#84c0a0', fps = 15 }: MatrixRainPr const setCanvasSize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight + 120; - ctx.fillStyle = '#0d1117'; + ctx.fillStyle = bgColor; ctx.fillRect(0, 0, canvas.width, canvas.height); }; @@ -43,13 +61,13 @@ export default function MatrixRain({ color = '#84c0a0', fps = 15 }: MatrixRainPr initDrops(); const draw = () => { - ctx.fillStyle = 'rgba(13, 17, 23, 0.2)'; + ctx.fillStyle = fadeColor; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.font = `bold ${fontSize}px monospace`; for (let i = 0; i < drops.length; i++) { const text = Math.random() > 0.5 ? '0' : '1'; - ctx.fillStyle = color; + ctx.fillStyle = textColor; ctx.fillText(text, i * fontSize, drops[i] * fontSize); if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) { @@ -75,14 +93,16 @@ export default function MatrixRain({ color = '#84c0a0', fps = 15 }: MatrixRainPr clearInterval(intervalId); window.removeEventListener('resize', handleResize); }; - }, [color, fps]); + }, [color, fps, resolvedTheme, mounted]); // Re-run effect if theme changes return ( -
- +
+ {mounted && ( + + )}
); } \ No newline at end of file diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx index 4f00d4f..952f05a 100644 --- a/app/components/Navbar.tsx +++ b/app/components/Navbar.tsx @@ -3,51 +3,61 @@ 'use client'; import Link from 'next/link'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import Image from 'next/image'; +import { useTheme } from 'next-themes'; +import { FaSun, FaMoon } from 'react-icons/fa'; export default function Navbar() { const [isExpanded, setIsExpanded] = useState(false); + const { theme, setTheme } = useTheme(); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => setMounted(true), 0); + return () => clearTimeout(timer); + }, []); const toggleMenu = () => setIsExpanded(!isExpanded); const closeMenu = () => setIsExpanded(false); return ( -
-