Skip to content
Merged
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
18 changes: 13 additions & 5 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export default function Navbar() {
const navigate = useNavigate();
const location = useLocation();
const { theme, toggleTheme } = useTheme();
const nextThemeLabel = theme === 'light' ? 'rose comfort' : theme === 'rose' ? 'dark' : 'light';
const themeIcon = theme === 'light'
? <Heart size={18} />
: theme === 'rose'
? <Moon size={18} />
: <Sun size={18} />;

useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => setUser(currentUser));
Expand Down Expand Up @@ -111,9 +117,10 @@ export default function Navbar() {
<button
onClick={toggleTheme}
className="theme-toggle-btn p-2 rounded-xl transition-all duration-300"
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} mode`}
aria-label={`Switch to ${nextThemeLabel} mode`}
title={`Switch to ${nextThemeLabel} mode`}
>
{theme === 'light' ? <Moon size={18} /> : <Sun size={18} />}
{themeIcon}
</button>

{user ? (
Expand Down Expand Up @@ -153,9 +160,10 @@ export default function Navbar() {
<button
onClick={toggleTheme}
className="theme-toggle-btn p-2 rounded-xl transition-all duration-300"
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} mode`}
aria-label={`Switch to ${nextThemeLabel} mode`}
title={`Switch to ${nextThemeLabel} mode`}
>
{theme === 'light' ? <Moon size={18} /> : <Sun size={18} />}
{themeIcon}
</button>

<button
Expand Down Expand Up @@ -518,4 +526,4 @@ export default function Navbar() {
`}</style>
</>
);
}
}
14 changes: 11 additions & 3 deletions src/components/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Moon, Sun } from 'lucide-react';
import { Heart, Moon, Sun } from 'lucide-react';
import { useTheme } from '../context/ThemeContext';

export default function ThemeToggle() {
const { theme, toggleTheme } = useTheme();
const nextThemeLabel = theme === 'light' ? 'rose comfort' : theme === 'rose' ? 'dark' : 'light';

return (
<button
onClick={toggleTheme}
className="relative p-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-all duration-200"
aria-label={`Switch to ${theme === 'light' ? 'dark' : 'light'} mode`}
aria-label={`Switch to ${nextThemeLabel} mode`}
title={`Switch to ${nextThemeLabel} mode`}
>
<div className="relative w-5 h-5">
<Sun
Expand All @@ -23,7 +25,13 @@ export default function ThemeToggle() {
}`}
size={20}
/>
<Heart
className={`absolute inset-0 transition-all duration-300 ${
theme === 'rose' ? 'scale-100 rotate-0 opacity-100' : 'scale-0 -rotate-90 opacity-0'
}`}
size={20}
/>
</div>
</button>
);
}
}
13 changes: 9 additions & 4 deletions src/context/ThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { createContext, useContext, useEffect, useState } from 'react';

type Theme = 'light' | 'dark';
type Theme = 'light' | 'rose' | 'dark';

interface ThemeContextType {
theme: Theme;
Expand All @@ -15,7 +15,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Check if user has a theme preference in localStorage
const savedTheme = localStorage.getItem('theme') as Theme;
if (savedTheme) {
if (savedTheme === 'light' || savedTheme === 'rose' || savedTheme === 'dark') {
setTheme(savedTheme);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
// Or use system preference
Expand All @@ -26,11 +26,16 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Update document class and localStorage when theme changes
document.documentElement.classList.toggle('dark', theme === 'dark');
document.documentElement.classList.toggle('rose', theme === 'rose');
localStorage.setItem('theme', theme);
}, [theme]);

const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
setTheme(prev => {
if (prev === 'light') return 'rose';
if (prev === 'rose') return 'dark';
return 'light';
});
};

return (
Expand All @@ -46,4 +51,4 @@ export function useTheme() {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
}
115 changes: 115 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,121 @@
}
}

/* Rose Comfort theme: soft light mode variant with accessible warm contrast */
.rose body {
background:
radial-gradient(circle at top left, rgba(251, 207, 232, 0.35), transparent 32rem),
linear-gradient(135deg, #fff7fb 0%, #fff1f5 45%, #fffafa 100%);
color: #3f1d2e;
transition: background-color 200ms ease, color 200ms ease;
}

.rose .bg-white,
.rose .bg-gray-50 {
background-color: rgba(255, 250, 252, 0.94) !important;
}

.rose .bg-gray-100,
.rose .bg-pink-50 {
background-color: rgba(255, 228, 236, 0.82) !important;
}

.rose .text-black,
.rose .text-gray-900,
.rose .text-gray-800,
.rose .text-gray-700 {
color: #3f1d2e !important;
}

.rose .text-gray-600,
.rose .text-gray-500 {
color: #6d3551 !important;
}

.rose .border-gray-100,
.rose .border-gray-200,
.rose .border-gray-300 {
border-color: rgba(244, 114, 182, 0.28) !important;
}

.rose input,
.rose textarea,
.rose select {
background-color: rgba(255, 250, 252, 0.92) !important;
border-color: rgba(244, 114, 182, 0.36) !important;
color: #3f1d2e !important;
}

.rose input::placeholder,
.rose textarea::placeholder {
color: #9d6b80 !important;
}

.rose .shadow-md,
.rose .shadow-lg,
.rose .shadow-xl,
.rose .shadow-2xl {
box-shadow: 0 18px 45px -24px rgba(190, 24, 93, 0.38) !important;
}

.rose .navbar-glass {
background: rgba(255, 238, 246, 0.76) !important;
border-bottom-color: rgba(236, 72, 153, 0.26) !important;
box-shadow: 0 8px 30px -12px rgba(190, 24, 93, 0.28),
0 1px 0 0 rgba(255, 255, 255, 0.7) inset !important;
}

.rose .navbar-scrolled {
background: rgba(255, 228, 240, 0.88) !important;
box-shadow: 0 12px 34px -12px rgba(190, 24, 93, 0.34),
0 1px 0 0 rgba(255, 255, 255, 0.72) inset !important;
}

.rose .nav-link,
.rose .theme-toggle-btn,
.rose .hamburger-btn {
color: #9d174d !important;
}

.rose .theme-toggle-btn,
.rose .hamburger-btn {
background: rgba(255, 255, 255, 0.68) !important;
border-color: rgba(236, 72, 153, 0.32) !important;
}

.rose .mobile-menu-glass {
background: rgba(255, 247, 251, 0.94) !important;
}

.rose .from-pink-500,
.rose .from-pink-600 {
--tw-gradient-from: #fb7185 var(--tw-gradient-from-position) !important;
--tw-gradient-to: rgb(251 113 133 / 0) var(--tw-gradient-to-position) !important;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) !important;
}

.rose .from-purple-600 {
--tw-gradient-from: #c026d3 var(--tw-gradient-from-position) !important;
--tw-gradient-to: rgb(192 38 211 / 0) var(--tw-gradient-to-position) !important;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to) !important;
}

.rose .to-purple-600,
.rose .to-rose-500 {
--tw-gradient-to: #be185d var(--tw-gradient-to-position) !important;
}

.rose .bg-pink-500,
.rose .hover\:bg-pink-600:hover {
background-color: #e11d48 !important;
}

.rose .text-pink-500,
.rose .text-pink-600,
.rose .hover\:text-pink-600:hover {
color: #be185d !important;
}

/* -- Customized Scrollbar --*/
::-webkit-scrollbar {
width: 8px;
Expand Down
Loading