Portfolio website for Vanessa Depraute - Senior Full-Stack JavaScript Developer with 19+ years of experience. Specializing in React, TypeScript, WebGL, and real-time systems. Based in Paris, France.
πΌ For complete professional profile, experience, and client portfolio, see VAVA_PROFILE.md
Frontend app built with Vite + React + TypeScript.
pnpm install
pnpm devpnpm dev- local dev serverpnpm build- typecheck + production buildpnpm lint- lint the codebasepnpm preview- preview production build
- Entry point:
src/main.tsx(RouterProvider, ThemeProvider, Vercel Analytics) - Routing:
src/routes.tsx/rendersHomePage(English version, no redirect)/frrendersHomePage(French version)/blogrendersBlogPage(English version)/fr/blogrendersBlogPage(French version)/enand/en/blogredirect to/and/blog(301 permanent redirects for SEO compatibility)*rendersNotFoundPage
- Layout:
src/components/Layout/Layout.tsxwraps pages and adds a global blur layer. - i18n:
src/i18n/config.ts+src/locales/en.jsonandsrc/locales/fr.json.- Language detection: based on URL path (
/fr/*= French, otherwise = English) - No automatic redirects (SEO-friendly approach)
- Language detection: based on URL path (
- SEO:
src/components/SEOHead/SEOHead.tsxmanages multilingual SEO- Automatically generates
hreflangtags for language alternatives - Helps Google understand and index both language versions
- Included in
HomePageandBlogPage
- Automatically generates
- Theme:
src/contexts/ThemeContext.tsxtogglesdark-mode/light-modeondocumentElement. - Styling: Tailwind v4 + custom CSS (
src/index.css,src/App.css, and component CSS files).- Brand Colors: defined in
src/index.cssas--color-primaryand--color-secondary. - Single Source of Truth: These CSS variables are the ONLY source of truth for brand colors. Do not hardcode hex values in components or CSS. Change them in
index.cssto update the entire app.
- Brand Colors: defined in
- Assets:
public/for static files served as-is.src/assets/for bundled assets imported by components.
src/pages/- route-level pages (Home, Blog, NotFound)src/components/- site-specific UIsrc/components/ui/- shared UI primitives and effectssrc/components/magicui/andsrc/registry/magicui/- animated UI elementssrc/TerminalDemo.tsxandsrc/components/TerminalInterests.tsx- terminal-style content blockssrc/contexts/ThemeContext.tsx- theme state and persistencesrc/i18n/config.ts- language setup (route-driven)
The site uses a clean, SEO-friendly URL structure:
- English (default):
/and/blog - French:
/frand/fr/blog - Legacy redirects:
/en/*β/*(301 permanent redirects)
Language is detected from the URL path in each page component:
- Pages use
useLocation()to detect if the path starts with/fr - If detected,
i18n.changeLanguage("fr")is called - Otherwise, defaults to English
Component: src/components/SEOHead/SEOHead.tsx
Automatically generates hreflang tags for multilingual SEO:
<link rel="alternate" hreflang="en" href="https://vanessadepraute.dev/" />
<link rel="alternate" hreflang="fr" href="https://vanessadepraute.dev/fr" />
<link rel="alternate" hreflang="x-default" href="https://vanessadepraute.dev/" />This tells Google:
- The relationship between language versions
- Which version to show for each language preference
- The default version for unmatched languages
- β
No redirect on root path (Google can index
/) - β Clean URLs without language prefixes for English
- β
Proper
hreflangtags for international SEO - β
301 redirects preserve existing
/enlinks - β Standard web convention (root = primary language)
The site automatically generates SEO metadata at build time.
File: vite.config.ts (sitemapPlugin)
A Vite plugin generates sitemap.xml during each build with the current date as lastmod. This signals to Google that the site is actively maintained.
- Automatically updates
lastmodto the build date - Includes all pages with proper
hreflangtags - No manual maintenance required
Files: src/components/Footer/Footer.tsx, src/components/Layout/Layout.tsx
The footer displays a discrete "Last update" timestamp that updates on every build:
- English: "Last update: [Month Day, Year at Time]"
- French: "Dernière mise à jour : [Jour Mois Année à Heure]"
The build date is injected via Vite's define option (BUILD_DATE constant) and automatically formatted based on the current language.
File: src/components/Layout/Layout.tsx
The bottom blur effect (GradualBlur component) fades out progressively when approaching the end of the page:
- Full opacity when far from bottom
- Starts fading at 400px from bottom
- Fully invisible at 100px from bottom
This creates a smoother experience and reveals the footer content without overlay.
- Vite config:
vite.config.ts(alias@->src,.glbasset handling). - Tailwind config:
tailwind.config.js. - shadcn/ui config:
components.json. - ESLint config:
eslint.config.js.
vercel.jsondisables git deployments and sets a Basic-Auth header for all routes.
The project uses a comprehensive error handling strategy with Sentry for monitoring.
- Sentry: Configured in
src/main.tsxfor error tracking, session replays, and performance monitoring - ErrorBoundary:
src/components/ErrorBoundary/ErrorBoundary.tsxcatches React rendering errors - Hooks:
src/hooks/useErrorHandler.tsprovidesuseErrorHandleranduseAsyncError - Utilities:
src/utils/errorHandling.tsprovidescaptureError,safeAsync,safeSync,logMessage
import { useErrorHandler } from "@/hooks/useErrorHandler";
function MyComponent() {
const { handleError, isError, getErrorMessage } = useErrorHandler("MyComponent");
useEffect(() => {
try {
// Your code
} catch (error) {
handleError(error, { action: "fetch_data" });
}
}, [handleError]);
}import { useAsyncError } from "@/hooks/useErrorHandler";
function MyComponent() {
const { executeAsync, isLoading, isError } = useAsyncError("MyComponent");
const fetchData = async () => {
const result = await executeAsync(
async () => await fetch("/api/data").then(r => r.json()),
{ action: "fetch_data" }
);
};
}import { captureError, safeAsync, safeSync, logMessage, ErrorSeverity } from "@/utils/errorHandling";
// Manual error capture
captureError(error, { component: "MyComponent", action: "user_action" }, ErrorSeverity.Error);
// Safe wrappers
const result = await safeAsync(() => fetchData(), { action: "fetch" });
const value = safeSync(() => JSON.parse(data), { action: "parse" });
// Logging
logMessage("User completed action", ErrorSeverity.Info, { userId: "123" });- Wrap risky operations in try-catch (localStorage, JSON.parse, API calls)
- Use hooks (
useErrorHandler,useAsyncError) in React components - Add context to errors (action, component, relevant data)
- Don't over-catch: Let ErrorBoundary handle rendering errors
Error UI messages are in src/locales/{en,fr}.json under the errors key.
The homepage features a dedicated projects section showcasing featured work.
- Component:
src/components/ProjectCard/ProjectCard.tsx- Reusable project card with Apple Card design - Data: Project information stored in
src/locales/{en,fr}.jsonunder theprojectskey - Integration: Rendered in
HomePage.tsxwithin the LightRays section
{
"projects": {
"title": "Featured Projects",
"subtitle": "Recent work and ongoing projects",
"items": [
{
"id": "projectId",
"link": "https://example.com",
"github": "https://github.com/user/repo",
"name": "Project Name",
"year": "2025",
"status": "In Development",
"description": "Project description...",
"techStack": ["React", "Next.js", "..."],
"highlights": ["Feature 1", "Feature 2", "..."]
}
]
}
}- Dark/Light Mode: Automatic theme detection and styling
- Responsive Design: 3-column desktop, 2-column tablet, 1-column mobile
- Status Badges: Color-coded badges (In Development, Production, etc.)
- Tech Stack Tags: Display technologies used in each project
- Glassmorphism: Apple Card-inspired design with backdrop blur
- Project Images: Optional image display with zoom hover effect
- Project Links: "View Project" and "Source Code" buttons with icons (Lucide React)
- Hover Effects: Card lift animation and image zoom on hover
- Add project data to
src/locales/en.jsonandsrc/locales/fr.jsonunderprojects.items - Include all required fields:
name,year,status,description,techStack,highlights - (Optional) Add
id,link, andgithubfields:id: Unique identifier used to map to images inHomePage.tsx(e.g.,"outOfBurn")link: External project URL (displays "View Project" button)github: GitHub repository URL (displays "Source Code" button)
- If adding an image, import it in
HomePage.tsxand add to theprojectImagesmapping - The component will automatically render the new project with all features
The website features an interactive 3D model viewer (src/components/ModelViewer/ModelViewer.tsx) that adapts to the user's device.
- Adaptive Input: Automatically detects device type and uses appropriate input method
- Desktop: Mouse movement controls model rotation
- Mobile: Gyroscope/device orientation controls model rotation
- Smooth Interpolation: Both input methods use smooth lerp-based transitions
- Permission Handling: Automatically requests DeviceOrientation permission on iOS 13+ devices
- Configurable: Extensive camera and interaction configuration options
<ModelViewer
modelPath="/toon_cat_free.glb"
playAnimation={true}
cameraConfig={{
followMouse: true, // Enable mouse/gyroscope following
mouseFollowSpeed: 0.2, // Interpolation speed (0-1)
mouseFollowRange: 0.45, // Max rotation range in radians
mouseFollowAxis: 'both' // 'x', 'y', or 'both'
}}
/>On mobile devices, the component:
- Detects mobile via user agent and screen width
- Requests DeviceOrientation permission (required for iOS 13+)
- Maps device tilt angles (beta/gamma) to model rotation
- Uses the same smooth interpolation as desktop mouse movement
The gyroscope sensitivity is automatically calibrated for natural interaction.
The codebase follows a comprehensive documentation strategy:
All source files include JSDoc headers with:
@file- Filename and emoji identifier@description- Comprehensive explanation of what the file does and why@functions- List of exported functions (usingβbullets)@exports- What the file exports@see- Related files when relevant
Code is annotated with inline comments using the π emoji prefix:
- Explain the "why" and "how" of complex logic
- Document configuration options and their purpose
- Clarify architectural decisions
- Note gotchas and edge cases
- Language: All documentation is in English
- Format: Plain english sentences, not terse abbreviations
- Purpose: Focus on explaining context and reasoning, not just describing what code does
- Consistency: Use π emoji for all inline comments
Example:
/**
* @file HomePage.tsx
* @description π Main homepage component - Portfolio landing page
*
* This is the primary landing page showcasing projects, skills, and bio.
*
* @functions
* β HomePage β Main component rendering the portfolio page
*
* @exports default - HomePage component
*/
// π Error handling integration for Sentry reporting
const { handleError } = useErrorHandler("HomePage");- Use pnpm only (this repo has a
pnpm-lock.yaml). src/App.tsxexists but is not wired to the router; usesrc/pages/instead.- Read
AGENTS.mdbefore starting any task.
This portfolio site is built with modern web technologies and best practices:
- Tech Stack: Vite + React + TypeScript + Tailwind CSS v4
- Features: Multilingual (EN/FR), Dark/Light mode, 3D models, SEO optimized
- Performance: Code-splitting, lazy loading, optimized assets
- Quality: Sentry error tracking, comprehensive error handling
- Documentation: All code documented with JSDoc and inline comments
For questions or collaboration inquiries, visit the website to get in touch!