# Story 2.8: Page Parcours - Timeline narrative Status: review ## Story As a visiteur, I want découvrir le parcours professionnel du développeur sous forme de timeline, so that je comprends son évolution et son expérience. ## Acceptance Criteria 1. **Given** le visiteur accède à `/parcours` (FR) ou `/en/journey` (EN) **When** la page se charge **Then** une timeline verticale affiche les étapes chronologiques du parcours 2. **And** chaque étape affiche : date, titre, description narrative traduite 3. **And** sur desktop : les étapes alternent gauche/droite pour un effet visuel dynamique 4. **And** sur mobile : les étapes sont linéaires (toutes du même côté) 5. **And** une animation d'apparition au scroll est présente (respectant `prefers-reduced-motion`) 6. **And** des icônes ou images illustrent les étapes clés 7. **And** le contenu est bilingue (FR/EN) et chargé depuis l'API ou fichiers i18n 8. **And** les meta tags SEO sont dynamiques pour cette page 9. **And** la police serif narrative est utilisée pour les descriptions ## Tasks / Subtasks - [x] **Task 1: Décider de la source de données** (AC: #7) - [x] Option A : Fichiers i18n (données statiques) - CHOISI - [x] Le parcours change rarement, pas besoin de CRUD - [x] **Task 2: Créer les données du parcours dans i18n** (AC: #2, #7) - [x] Ajouter les clés `journey.milestones` dans fr.json et en.json - [x] Structure : date, title, description, icon - [x] 7 étapes du parcours professionnel (2018-2025) - [x] **Task 3: Créer le composant TimelineItem** (AC: #2, #6, #9) - [x] Créer `frontend/app/components/feature/TimelineItem.vue` - [x] Props : milestone (date, title, description, icon) - [x] Afficher l'icône, la date, le titre et la description - [x] Utiliser font-narrative pour la description - [x] **Task 4: Créer la page parcours.vue** (AC: #1, #3, #4) - [x] Créer `frontend/app/pages/parcours.vue` - [x] Charger les milestones depuis i18n avec tm() - [x] Layout timeline vertical avec ligne centrale gradient - [x] Desktop : alternance gauche/droite - [x] Mobile : toutes les étapes à gauche de la ligne - [x] **Task 5: Implémenter l'animation au scroll** (AC: #5) - [x] Créer composable `useIntersectionObserver()` - [x] Animation fade-in + slide-up pour chaque étape - [x] Respecter prefers-reduced-motion - [x] Points de la timeline s'illuminent au passage - [x] **Task 6: Design de la timeline** (AC: #3, #4) - [x] Ligne centrale verticale gradient sky-500 - [x] Points de connexion sur la ligne (circles sky-400) - [x] Cards glassmorphism avec bordure subtile - [x] Responsive : adaptation mobile - [x] **Task 7: Meta tags SEO** (AC: #8) - [x] Titre : "Parcours | Skycel" - [x] Description du parcours - [x] **Task 8: Tests et validation** - [x] Build validé - [x] Traductions FR et EN complètes - [x] Animation au scroll fonctionnelle ## Dev Notes ### Structure des données dans i18n **fr.json :** ```json { "journey": { "title": "Mon Parcours", "pageTitle": "Parcours | Skycel", "pageDescription": "Découvrez le parcours professionnel de Célian, de ses débuts à aujourd'hui.", "milestones": [ { "date": "2018", "title": "Premiers pas en développement", "description": "Découverte du code à travers des projets personnels. HTML, CSS, JavaScript deviennent mes nouveaux compagnons de route. L'étincelle est là.", "icon": "🚀" }, { "date": "2019", "title": "Formation intensive", "description": "Plongée dans le monde du développement web professionnel. Apprentissage de frameworks modernes, bonnes pratiques, et méthodologies agiles.", "icon": "📚" }, { "date": "2020", "title": "Premiers clients", "description": "Lancement en freelance. Premiers projets concrets, premiers défis réels. Chaque client m'apprend quelque chose de nouveau.", "icon": "💼" }, { "date": "2021", "title": "Spécialisation Vue.js & Laravel", "description": "Le duo qui change tout. Vue.js côté front, Laravel côté back. Une stack qui me permet de créer des expériences web complètes et performantes.", "icon": "⚡" }, { "date": "2022", "title": "Création de la micro-entreprise", "description": "Officialisation de l'aventure entrepreneuriale. L'araignée devient la mascotte, le Bug devient le guide. L'identité Skycel prend forme.", "icon": "🕷️" }, { "date": "2023-2024", "title": "Projets ambitieux", "description": "Des applications web complexes aux sites e-commerce, chaque projet repousse les limites. TypeScript, Nuxt 4, et une obsession pour la qualité.", "icon": "🎯" }, { "date": "2025", "title": "Aujourd'hui", "description": "Ce portfolio que vous explorez. Une aventure en soi, qui reflète ma passion pour créer des expériences web mémorables. Et ce n'est que le début...", "icon": "✨" } ] } } ``` **en.json :** ```json { "journey": { "title": "My Journey", "pageTitle": "Journey | Skycel", "pageDescription": "Discover Célian's professional journey, from the beginning to today.", "milestones": [ { "date": "2018", "title": "First steps in development", "description": "Discovering code through personal projects. HTML, CSS, JavaScript became my new travel companions. The spark was there.", "icon": "🚀" }, { "date": "2019", "title": "Intensive training", "description": "Deep dive into professional web development. Learning modern frameworks, best practices, and agile methodologies.", "icon": "📚" }, { "date": "2020", "title": "First clients", "description": "Starting as a freelancer. First real projects, first real challenges. Each client teaches me something new.", "icon": "💼" }, { "date": "2021", "title": "Specialization in Vue.js & Laravel", "description": "The game-changing duo. Vue.js on the front, Laravel on the back. A stack that allows me to create complete, performant web experiences.", "icon": "⚡" }, { "date": "2022", "title": "Creating the micro-enterprise", "description": "Making the entrepreneurial adventure official. The spider becomes the mascot, the Bug becomes the guide. The Skycel identity takes shape.", "icon": "🕷️" }, { "date": "2023-2024", "title": "Ambitious projects", "description": "From complex web applications to e-commerce sites, each project pushes boundaries. TypeScript, Nuxt 4, and an obsession with quality.", "icon": "🎯" }, { "date": "2025", "title": "Today", "description": "This portfolio you're exploring. An adventure in itself, reflecting my passion for creating memorable web experiences. And this is just the beginning...", "icon": "✨" } ] } } ``` ### Composable useIntersectionObserver ```typescript // frontend/app/composables/useIntersectionObserver.ts export interface UseIntersectionObserverOptions { threshold?: number rootMargin?: string once?: boolean } export function useIntersectionObserver( target: Ref, options: UseIntersectionObserverOptions = {} ) { const { threshold = 0.1, rootMargin = '0px', once = true } = options const isVisible = ref(false) let observer: IntersectionObserver | null = null onMounted(() => { if (!target.value) return observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { isVisible.value = true if (once && observer) { observer.unobserve(entry.target) } } else if (!once) { isVisible.value = false } }) }, { threshold, rootMargin } ) observer.observe(target.value) }) onUnmounted(() => { if (observer) { observer.disconnect() } }) return { isVisible } } ``` ### Composant TimelineItem ```vue ``` ### Page parcours.vue ```vue ``` ### Clés i18n supplémentaires **fr.json :** ```json { "journey": { "endMessage": "L'aventure continue... Qui sait où le code me mènera demain ?" } } ``` **en.json :** ```json { "journey": { "endMessage": "The adventure continues... Who knows where code will take me tomorrow?" } } ``` ### Design de la timeline ``` DESKTOP (alternance gauche/droite) : ┌─────────────────┐ │ 2018 │ │ Description │──●── └─────────────────┘ │ │ ──●────┼────┌─────────────────┐ │ │ 2019 │ │ │ Description │ │ └─────────────────┘ │ ┌─────────────────┐ │ │ 2020 │──●── │ Description │ │ └─────────────────┘ │ MOBILE (linéaire à droite) : │ ┌─────────────────┐ ●──│ 2018 │ │ │ Description │ │ └─────────────────┘ │ │ ┌─────────────────┐ ●──│ 2019 │ │ │ Description │ │ └─────────────────┘ ``` ### Dépendances **Cette story nécessite :** - Story 1.3 : Système i18n configuré - Story 1.4 : Layouts et routing **Cette story prépare pour :** - Aucune dépendance directe (dernière story de l'Epic 2) ### Project Structure Notes **Fichiers à créer :** ``` frontend/app/ ├── pages/ │ └── parcours.vue # CRÉER ├── components/feature/ │ └── TimelineItem.vue # CRÉER └── composables/ └── useIntersectionObserver.ts # CRÉER ``` **Fichiers à modifier :** ``` frontend/i18n/fr.json # AJOUTER journey.* frontend/i18n/en.json # AJOUTER journey.* ``` ### References - [Source: docs/planning-artifacts/epics.md#Story-2.8] - [Source: docs/planning-artifacts/ux-design-specification.md#Screen-Architecture-Summary] - [Source: docs/planning-artifacts/ux-design-specification.md#Typography-System] ### Technical Requirements | Requirement | Value | Source | |-------------|-------|--------| | Source données | Fichiers i18n | Décision technique | | Layout desktop | Alternance gauche/droite | Epics | | Layout mobile | Linéaire à droite | Epics | | Animation | IntersectionObserver + fade-in | Epics | | Police | font-narrative pour descriptions | UX Spec | ## Dev Agent Record ### Agent Model Used {{agent_model_name_version}} ### Debug Log References ### Completion Notes List ### Change Log | Date | Change | Author | |------|--------|--------| | 2026-02-04 | Story créée avec contexte complet | SM Agent | | 2026-02-06 | Implémentation complète: useIntersectionObserver, TimelineItem, page parcours, traductions | Claude Opus 4.5 | ### File List