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:
@@ -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 />
|
||||
|
||||
Reference in New Issue
Block a user