Enhances UI with i18n, theme persistence and navigation

Adds internationalization (i18n) support using i18next, enabling multi-language support.

Persists theme selection in local storage, providing a consistent user experience across sessions.

Implements smooth scrolling navigation to different sections of the page.

Introduces a "Projects" section using a routing system, with tabs for professional and personal endeavors and detailed views using framer-motion for smooth transitions.

Includes updated resume PDFs.
This commit is contained in:
Louis
2025-10-28 14:21:02 +01:00
parent 5eb0beafb3
commit 3e3a6dd125
27 changed files with 468 additions and 358 deletions

View File

@@ -10,8 +10,22 @@ import { ThemeMenu, ThemeType, ThemeSelected } from './ThemeMenu';
function App() {
const { t, i18n } = useTranslation();
const [theme, setTheme] = useState<ThemeType>('dark');
const [themeSelected, setThemeSelected] = useState<ThemeSelected>('system');
const [theme, setTheme] = useState<ThemeType>(() => {
const savedTheme = localStorage.getItem('theme');
return (savedTheme as ThemeType) || 'dark';
});
const [themeSelected, setThemeSelected] = useState<ThemeSelected>(() => {
const savedThemeSelected = localStorage.getItem('themeSelected');
return (savedThemeSelected as ThemeSelected) || 'system';
});
// Charger la langue sauvegardée au démarrage
useEffect(() => {
const savedLanguage = localStorage.getItem('language');
if (savedLanguage && (savedLanguage === 'fr' || savedLanguage === 'en')) {
i18n.changeLanguage(savedLanguage);
}
}, [i18n]);
useEffect(() => {
if (themeSelected === 'system') {
@@ -30,32 +44,51 @@ function App() {
setTheme(themeSelected);
document.documentElement.className = `theme-${themeSelected}`;
}
}, [themeSelected]);
// Sauvegarder le thème sélectionné
localStorage.setItem('themeSelected', themeSelected);
localStorage.setItem('theme', theme);
}, [themeSelected, theme]);
const toggleLanguage = () => {
const newLang = i18n.language === 'fr' ? 'en' : 'fr';
i18n.changeLanguage(newLang);
localStorage.setItem('language', newLang);
};
const handleThemeChange = (newTheme: ThemeSelected) => {
setThemeSelected(newTheme);
};
const scrollToSection = (id: string) => {
const section = document.getElementById(id);
if (section) {
section.scrollIntoView({ behavior: 'smooth' });
}
}
return (
<div className="min-h-screen text-secondary bg-primary transition-colors duration-300">
<div className="min-h-screen text-secondary bg-primary/80 transition-colors duration-100">
{/* Background Blobs */}
<div className="background-blobs">
<div className="blob blob-1"></div>
<div className="blob blob-2"></div>
<div className="blob blob-3"></div>
</div>
{/* Header */}
<header className='fixed top-0 left-0 right-0 z-50 flex justify-between items-center gap-4 text-sm w-full px-8 py-6 bg-primary/80 backdrop-blur-md border-b border-secondary/10'>
<h1 className='text-2xl font-bold'>
<span className='text-accent'>L</span>ouis <span className='text-accent'>E</span>MARD
Louis EMARD
</h1>
<nav className='flex gap-6 h-full justify-center items-center'>
<a href='#about' className='hover:text-accent transition-colors'>{t('nav.about')}</a>
<a href='#experience' className='hover:text-accent transition-colors'>{t('nav.experience')}</a>
<a href='#skills' className='hover:text-accent transition-colors'>{t('nav.skills')}</a>
<a href='#education' className='hover:text-accent transition-colors'>{t('nav.education')}</a>
<a href='#contact' className='hover:text-accent transition-colors'>{t('nav.contact')}</a>
<a onClick={() => scrollToSection('about')} className='hover:text-accent transition-colors cursor-pointer'>{t('nav.about')}</a>
<a onClick={() => scrollToSection('experience')} className='hover:text-accent transition-colors cursor-pointer'>{t('nav.projects')}</a>
<a onClick={() => scrollToSection('skills')} className='hover:text-accent transition-colors cursor-pointer'>{t('nav.skills')}</a>
<a onClick={() => scrollToSection('education')} className='hover:text-accent transition-colors cursor-pointer'>{t('nav.education')}</a>
<a onClick={() => scrollToSection('contact')} className='hover:text-accent transition-colors cursor-pointer'>{t('nav.contact')}</a>
{/* Language Toggle */}
<button
onClick={toggleLanguage}
@@ -74,7 +107,7 @@ function App() {
</header>
{/* Main Content */}
<main>
<main className='bg-'>
<Hero />
<About />
<Experience />