Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/DarkMode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Dark Mode Feature 🌙

## Overview

The portfolio website now supports **Dark Mode**, allowing users to switch between light and dark themes for better readability and reduced eye strain.

---

## How to Use Dark Mode

1. **Locate the Toggle Button**
The dark mode toggle is available in the **header/navbar** of the website.

2. **Switch Themes**
Click the toggle button to switch between **Light** and **Dark** modes.
The selected theme is **saved locally** and will persist on your next visit.

---

## Screenshots

### Light Mode

![Light Mode Screenshot](path/to/lightmode.png)

### Dark Mode

![Dark Mode Screenshot](path/to/darkmode.png)

---

## Implementation Details

- **Files Involved:**

- `components/Header.js` – Contains the dark mode toggle button.
- `styles/globals.css` – Includes CSS variables for dark and light themes.
- `utils/theme.js` – Handles saving theme preference in `localStorage`.

- **Code Snippet for Toggle Button:**

```javascript
// Toggle dark mode
const toggleTheme = () => {
const currentTheme = document.documentElement.classList.contains('dark')
? 'dark'
: 'light';
if (currentTheme === 'dark') {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
}
};
```
36 changes: 36 additions & 0 deletions src/components/DarkModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';
import { useEffect, useState } from "react";

export default function DarkModeToggle() {
const [theme, setTheme] = useState(localStorage.getItem("theme") || "light");

const toggleTheme = () => setTheme(theme === "dark" ? "light" : "dark");

// Apply theme and persist
useEffect(() => {
if (theme === "dark") document.documentElement.classList.add("dark");
else document.documentElement.classList.remove("dark");
localStorage.setItem("theme", theme);
}, [theme]);

// Keyboard shortcut: Ctrl+D (Windows/Linux) or Cmd+D (Mac)
useEffect(() => {
const handleKey = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "d") {
e.preventDefault();
toggleTheme();
}
};
window.addEventListener("keydown", handleKey);
return () => window.removeEventListener("keydown", handleKey);
}, [theme]);

return (
<button
onClick={toggleTheme}
className="p-2 rounded-md bg-bg-secondary text-text hover:bg-accent-light transition"
>
{theme === "dark" ? "🌙 Dark" : "☀️ Light"}
</button>
);
}
22 changes: 11 additions & 11 deletions src/components/buttons/NavButton.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
'use client';


type Props = {
onClick: (event: React.MouseEvent) => void;
className?: string;
navbarCollapsed: boolean;
};

const NavButton = ({ onClick, className, navbarCollapsed }: Props) => {
const classes = `bg-accent h-0.5 duration-200 ${
navbarCollapsed ? 'absolute' : ''
}`;
const classes = `bg-accent h-0.5 duration-200 ${navbarCollapsed ? 'absolute' : ''
}`;

return (
<button
className={`${className} w-7 h-7 group transition focus:outline-none`}
onClick={onClick}
>
<div
className={`flex flex-col items-end relative ${
navbarCollapsed
? 'space-y-0 rotate-90 duration-300 delay-100'
: 'group-hover:space-y-1 group-focus:space-y-1 space-y-1.5'
}`}
className={`flex flex-col items-end relative ${navbarCollapsed
? 'space-y-0 rotate-90 duration-300 delay-100'
: 'group-hover:space-y-1 group-focus:space-y-1 space-y-1.5'
}`}
>
<div
className={`w-7 ${classes} ${navbarCollapsed ? 'rotate-45' : ''}`}
></div>
<div
className={`${classes} w-6 ${
navbarCollapsed ? 'opacity-0' : 'opacity-100'
}`}
className={`${classes} w-6 ${navbarCollapsed ? 'opacity-0' : 'opacity-100'
}`}
></div>
<div
className={`${classes} ${navbarCollapsed ? '-rotate-45 w-7' : 'w-5'}`}
Expand All @@ -39,3 +38,4 @@ const NavButton = ({ onClick, className, navbarCollapsed }: Props) => {
};

export default NavButton;

1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export { default as ProjectCard } from './ui/ProjectCard';
// export { default as ShowLottie } from './ui/ShowLottie';
export { default as Sidebar } from './ui/Sidebar';
export { default as Wrapper } from './ui/Wrapper';
export { default as DarkModeToggle } from "./DarkModeToggle";
6 changes: 5 additions & 1 deletion src/containers/layout/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
'use client';
import { DarkModeToggle } from "./components";


import { navbarSection } from '@/lib/content/navbar';
import { author } from '@/lib/content/portfolio';
import useWindowWidth from '@/lib/hooks/use-window-width';
Expand Down Expand Up @@ -137,7 +140,8 @@ const Navbar = () => {
{cta.title}
</Button>
)}
<DarkModeButton
<DarkModeToggle

onClick={() => setNavbarCollapsed(false)}
variants={slideIn({
delay: ANIMATION_DELAY + (navLinks.length + 1) / 10,
Expand Down
Loading