Setup complet de l'infrastructure projet : - Frontend Nuxt 4 (SSR, TypeScript, i18n, Pinia, TailwindCSS) - Backend Laravel 12 API-only avec middleware X-API-Key et CORS - Design tokens (sky-dark, sky-accent, sky-text) et polices (Merriweather, Inter) - Documentation planning et implementation artifacts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
42 KiB
stepsCompleted, status, validatedAt, inputDocuments
| stepsCompleted | status | validatedAt | inputDocuments | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
complete | 2026-02-03 |
|
skycel - Epic Breakdown
Overview
This document provides the complete epic and story breakdown for skycel, decomposing the requirements from the PRD, UX Design if it exists, and Architecture requirements into implementable stories.
Requirements Inventory
Functional Requirements
- FR1 : Le système offre une double entrée au visiteur : "Partir à l'aventure" (expérience complète) ou "Je n'ai pas le temps" (mode express avec roadmap)
- FR2 : Les transitions entre pages sont animées de manière seamless via Nuxt transitions, créant une impression de "changement de zone" immersive
- FR3 : Un narrateur-guide accompagne le visiteur avec des textes contextuels tout au long de l'expérience
- FR4 : Une carte interactive (Konva.js) permet la navigation non-linéaire et affiche la progression du visiteur
- FR5 : Un arbre de compétences interactif (vis.js) visualise les skills avec niveaux évoluant selon les projets
- FR6 : Les compétences sont cliquables et mènent directement aux projets qui les utilisent
- FR7 : Les témoignages s'affichent sous forme de dialogues PNJ style Zelda avec avatar, bulles, effet typewriter et personnalités variées
- FR8 : Une barre de progression globale indique l'avancement dans l'exploration du site
- FR9 : Le système propose 2-3 choix binaires créant 4-8 parcours narratifs différents, tous menant au contact
- FR10 : Un challenge/puzzle accessible doit être résolu pour accéder au formulaire de contact (avec système d'indices)
- FR11 : Des easter eggs cachés récompensent l'exploration avec des snippets de code ou anecdotes
- FR12 : La progression est sauvegardée automatiquement en LocalStorage pour permettre la reprise
- FR13 : Le site supporte deux langues (FR par défaut + EN) avec détection par URL (/en/...)
- FR14 : Le formulaire de contact est présenté comme la récompense finale "Tu m'as trouvé !" avec célébration
NonFunctional Requirements
- NFR1 : Le bundle JS total (Nuxt + Konva + vis.js) ne doit pas dépasser 170kb gzippé, avec lazy-loading des composants lourds
- NFR2 : Le temps de chargement initial (LCP) doit rester sous 2.5 secondes sur connexion 3G
- NFR3 : Le site doit être responsive et offrir une expérience adaptée mobile (carte simplifiée en Chemin Libre vertical)
- NFR4 : Le site doit fonctionner sur les navigateurs modernes (Chrome, Firefox, Safari, Edge — 2 dernières versions)
- NFR5 : Les URLs doivent être SEO-friendly et le contenu principal accessible aux crawlers (SSR Nuxt 4)
- NFR6 : Les animations doivent respecter
prefers-reduced-motionpour l'accessibilité - NFR7 : Le système i18n utilise @nuxtjs/i18n avec fichiers JSON, rendu SSR pour SEO optimal
- NFR8 : Les images sont optimisées en WebP avec lazy loading
Additional Requirements
Architecture :
- Starter template : Nuxt 4 (
npx nuxi@latest init) + Laravel 12 (composer create-project) — impacte Epic 1 Story 1 - Structure monorepo
frontend/(Nuxt 4 avec structureapp/) +api/(Laravel 12) - Table
translationscentralisée en MariaDB pour i18n du contenu dynamique (clés i18n dans les tables métier) - API REST avec API Key (
X-API-Key) + CORS strict (domaine frontend uniquement) - Store Pinia
useProgressionStoreavec persistance LocalStorage viapinia-plugin-persistedstate+ compatibilité SSR - Architecture composants frontend :
ui/(atomiques réutilisables),feature/(métier),layout/(structure page) - GSAP pour animations complexes (Swup.js abandonné — redondant avec transitions Nuxt natives)
- Transitions Nuxt natives (
pageTransition+<Transition>) + CSS animations - Sécurité contact : reCAPTCHA v3 (invisible) + honeypot + rate limiting Laravel (5 req/min par IP)
- Bandeau RGPD intégré à l'immersion narrative (dialogue PNJ/narrateur, style "pacte d'aventurier")
- Déploiement : Nginx → Node.js :3000 (Nuxt SSR) + PHP-FPM :9000 (Laravel API) + MariaDB :3306
- CI/CD : Script
deploy.shmanuel (git pull, build, migrate, restart) - Backups BDD : cron quotidien mysqldump + réplication distante rsync/scp, rétention 7 jours
- Environnements : production (
skycel.fr, brancheprod) + staging (staging.skycel.fr) - Cache : File cache Laravel (driver
file, pas de Redis) - Double validation : frontend (UX temps réel) + backend (Form Requests Laravel, source de vérité)
- Format réponse API : Laravel API Resources avec enveloppe
{ data, meta } - Gestion langue API : header
Accept-Language→ middleware Laravel → jointure table translations
UX :
- Système de héros : 3 personnages (Recruteur/Client/Dev) impactant vouvoiement, ton narrateur, type de challenges
- Narrateur = "Le Bug" (araignée, mascotte micro-entreprise) avec arc de révélation progressive (silhouette sombre → araignée complète en 5 étapes liées à la progression)
- Page résumé 30s (
/resume) : URL directe pour candidatures, accès sans passer par la landing - Déblocage contact après 2 zones visitées (pas de blocage excessif)
- Challenge final optionnel (jamais bloquant l'accès au contact)
- Challenge post-formulaire ("En attendant que le dev retrouve sa boîte mail...")
- "Monde de Code" : révélation finale — paysage en blocs de code ASCII art, avatar Célian au centre
- Navigation mobile : "Chemin Libre" vertical (ZoneCards scrollables) au lieu de Konva.js
- Bottom bar mobile fixe : Carte, Progression, Paramètres (thumb zone)
- Tous les feedbacks système passent par le narrateur (pas de toasts/notifications classiques)
- Headless UI / Radix UI pour composants standards accessibles (modals, tooltips, toggles, menus)
- Design tokens Tailwind :
sky-dark(noir→bleu),sky-accent(#fa784f orange),sky-text(blanc cassé→jaune) - Polices : serif élégante (narrateur/PNJ/narration) + sans-serif moderne (interface/UI)
- Approche Mobile First CSS
- WCAG AA : contraste ≥ 4.5:1, touch targets 44x44px min, skip links,
aria-live="polite"narrateur, navigation clavier complète - Sortie de zone par choix narratif du narrateur (pas de bouton "retour" froid)
- Couleurs par zone sur la carte (teintes uniques par section)
- Espacement aéré : spacing scale de 4px à 128px
Brainstorming :
- Schéma BDD détaillé : 8 tables (projects, skills, skill_project, testimonials, narrator_texts, easter_eggs, user_progress, translations)
- Personnalités PNJ : sage, sarcastique, enthousiaste, professionnel
- Easter eggs : triggers variés (click, hover, konami, scroll, sequence)
- Rewards easter eggs : snippet, anecdote, image, badge
- Sauvegarde cloud progression par email (optionnel, phase 2)
- Rappel email narratif après X jours (hors scope MVP)
FR Coverage Map
| FR | Epic | Description |
|---|---|---|
| FR1 | Epic 1 | Double entrée Aventure / Mode Express |
| FR2 | Epic 1 | Transitions de page animées seamless |
| FR3 | Epic 3 | Narrateur-guide contextuel |
| FR4 | Epic 3 | Carte interactive navigation non-linéaire |
| FR5 | Epic 2 | Arbre de compétences interactif |
| FR6 | Epic 2 | Compétences cliquables → projets liés |
| FR7 | Epic 2 | Témoignages dialogues PNJ |
| FR8 | Epic 3 | Barre de progression globale |
| FR9 | Epic 4 | Choix binaires créant parcours multiples |
| FR10 | Epic 4 | Challenge/puzzle avant contact |
| FR11 | Epic 4 | Easter eggs cachés |
| FR12 | Epic 3 | Sauvegarde progression LocalStorage |
| FR13 | Epic 1 | Bilingue FR/EN |
| FR14 | Epic 4 | Contact comme récompense finale |
Epic List
Epic 1 : Fondations & Double Entrée
Le visiteur arrive sur le site, choisit son héros et son mode (Aventure ou Express), et peut naviguer entre les pages avec des transitions immersives. Le site est bilingue et fonctionnel en SSR. FRs couverts : FR1, FR2, FR13
Epic 2 : Contenu & Découverte
Le visiteur explore les zones de contenu : projets (galerie + détail), compétences organisées par catégories avec liens vers les projets associés, témoignages en dialogues PNJ, et parcours en timeline narrative. FRs couverts : FR5, FR6, FR7
Epic 3 : Navigation Gamifiée & Progression
Le visiteur navigue via la carte interactive (Konva.js desktop / Chemin Libre mobile), est accompagné par le narrateur-guide (Le Bug), et voit sa progression sauvegardée automatiquement avec une barre XP. FRs couverts : FR3, FR4, FR8, FR12
Epic 4 : Chemins Narratifs, Challenge & Contact
Le visiteur fait des choix qui créent son parcours unique, relève un challenge optionnel, et accède à la révélation finale "Monde de Code" + formulaire de contact comme récompense narrative. Les easter eggs récompensent l'exploration. FRs couverts : FR9, FR10, FR11, FR14
Epic 1 : Fondations & Double Entrée
Le visiteur arrive sur le site, choisit son héros et son mode (Aventure ou Express), et peut naviguer entre les pages avec des transitions immersives. Le site est bilingue et fonctionnel en SSR.
Story 1.1 : Initialisation du monorepo et infrastructure
As a développeur, I want un projet monorepo Nuxt 4 + Laravel 12 initialisé avec les configurations de base, So that le développement peut commencer sur des fondations solides.
Acceptance Criteria:
Given un nouveau repository Git
When le projet est initialisé
Then la structure monorepo frontend/ (Nuxt 4) + api/ (Laravel 12) est en place
And Nuxt 4 est configuré avec SSR activé, TypeScript, et les modules @nuxtjs/i18n, @nuxtjs/tailwindcss, @pinia/nuxt, nuxt/image, @nuxtjs/sitemap
And Laravel 12 est configuré en mode API-only avec CORS autorisant le domaine frontend
And le middleware API Key (X-API-Key) est en place sur les routes API
And les fichiers .env.example existent pour frontend et backend
And TailwindCSS est configuré avec les design tokens (sky-dark, sky-accent #fa784f, sky-text)
And les polices sont définies (serif narrateur + sans-serif UI)
And le .gitignore est approprié pour les deux applications
Story 1.2 : Base de données et migrations initiales
As a développeur, I want le schéma de base de données MariaDB avec les tables nécessaires à l'Epic 1, So that l'API peut servir du contenu bilingue.
Acceptance Criteria:
Given une connexion MariaDB configurée dans Laravel
When php artisan migrate est exécuté
Then la table translations est créée (id, lang, key_name, value, timestamps) avec index unique (lang, key_name)
And la table projects est créée (id, slug, title_key, description_key, short_description_key, image, url, github_url, date_completed, is_featured, display_order, timestamps)
And la table skills est créée (id, slug, name_key, description_key, icon, category, max_level, display_order)
And la table skill_project est créée (id, skill_id, project_id, level_before, level_after, level_description_key) avec foreign keys
And les Models Eloquent sont définis avec leurs relations (Project belongsToMany Skill, etc.)
And des Seeders de base sont disponibles avec données de test en FR et EN
And php artisan db:seed fonctionne correctement
Story 1.3 : Système i18n frontend + API bilingue
As a visiteur, I want voir le site dans ma langue (FR ou EN), So that je comprends le contenu.
Acceptance Criteria:
Given le module @nuxtjs/i18n configuré avec stratégie prefix_except_default
When le visiteur accède à / ou /en
Then le contenu statique UI est affiché dans la langue correspondante via fichiers JSON (i18n/fr.json, i18n/en.json)
And les URLs FR sont par défaut (/, /projets, /competences, /contact)
And les URLs EN sont préfixées (/en, /en/projects, /en/skills, /en/contact)
And useI18n(), $t(), localePath(), switchLocalePath() fonctionnent en SSR
And les tags hreflang sont générés automatiquement dans le <head>
And l'attribut lang du <html> est dynamique (fr/en)
And le middleware Laravel extrait Accept-Language et joint la table translations pour le contenu dynamique
And les API Resources Laravel renvoient le contenu traduit selon la langue demandée
And le fallback est FR si langue non supportée
Story 1.4 : Layouts, routing et transitions de page
As a visiteur, I want une navigation fluide entre les pages avec des transitions immersives, So that l'expérience ressemble à un changement de zone, pas à un rechargement.
Acceptance Criteria:
Given la structure de pages Nuxt 4 (app/pages/)
When le visiteur navigue entre les pages
Then les transitions de page sont animées (fade + slide) via pageTransition dans nuxt.config.ts
And la navigation utilise <NuxtLink> pour l'hydration SPA (pas de rechargement)
And le layout par défaut (default.vue) inclut le header avec barre de progression (placeholder) et sélecteur de langue
And un layout minimal.vue existe pour le mode express
And le scrollBehavior est personnalisé (smooth scroll, retour position sauvegardée)
And prefers-reduced-motion désactive les animations de transition via media query CSS
And une page 404 (error.vue) bilingue est en place
And les meta tags SEO dynamiques fonctionnent via useHead() et useSeoMeta()
And le favicon est configuré
Story 1.5 : Landing page et choix du héros
As a visiteur, I want choisir entre l'aventure et le mode express, puis sélectionner mon héros, So that mon expérience est adaptée à mon profil et mon temps disponible.
Acceptance Criteria:
Given le visiteur arrive sur la landing page (/)
When la page se charge
Then deux CTA distincts sont visibles : "Partir à l'aventure" et "Mode express"
And un texte d'accroche intrigant bilingue est affiché
And une animation d'entrée subtile est présente (respectant prefers-reduced-motion)
And le design est responsive (mobile + desktop)
And au clic sur "Partir à l'aventure", le composant HeroSelector s'affiche avec 3 cards illustrées (Recruteur, Client, Développeur) avec nom et description courte
And le héros sélectionné est stocké dans le store Pinia useProgressionStore (champ hero)
And au clic sur "Mode express", le visiteur est redirigé vers la page résumé
And le HeroSelector est accessible au clavier (role="radiogroup", flèches pour naviguer, Enter pour sélectionner)
Story 1.6 : Store Pinia progression et bandeau RGPD
As a visiteur, I want que ma progression soit sauvegardée et que mon consentement soit respecté, So that je peux reprendre mon exploration et mes données sont protégées.
Acceptance Criteria:
Given le visiteur accède au site
When le consentement RGPD n'a pas encore été donné
Then un bandeau de consentement immersif s'affiche (style narratif/dialogue, pas un bandeau classique)
And le store Pinia useProgressionStore est initialisé avec : sessionId (UUID v4), hero, currentPath, visitedSections, completionPercent, easterEggsFound, challengeCompleted, contactUnlocked, narratorStage, choices, consentGiven
And la persistance LocalStorage est activée via pinia-plugin-persistedstate (uniquement après consentement)
And le store est compatible SSR (initialisation vide côté serveur, réhydratation client)
And si une progression existante est détectée, un message "Bienvenue à nouveau" est affiché
And l'action $reset() permet de réinitialiser la progression
Story 1.7 : Page résumé express et mode pressé
As a visiteur pressé ou recruteur, I want une vue condensée de toutes les informations essentielles, So that je peux évaluer le développeur en 30 secondes.
Acceptance Criteria:
Given le visiteur accède à /resume (FR) ou /en/resume (EN) directement ou via "Mode express"
When la page se charge
Then le contenu affiché comprend : nom, titre, photo/avatar, accroche (5s)
And les compétences clés avec stack technique sont visibles (10s)
And 3-4 projets highlights avec liens sont affichés (10s)
And un CTA de contact direct est visible (5s)
And un bouton discret "Voir l'aventure" invite à l'expérience complète
And la page est fonctionnelle en FR et EN
And les données sont chargées depuis l'API (projets, skills)
And les meta tags SEO sont optimisés pour cette page
And le layout minimal.vue est utilisé
Epic 2 : Contenu & Découverte
Le visiteur explore les zones de contenu : projets (galerie + détail), compétences organisées par catégories avec liens vers les projets associés, témoignages en dialogues PNJ, et parcours en timeline narrative.
Story 2.1 : Composant ProjectCard
As a développeur, I want un composant réutilisable de card de projet, So that je peux afficher les projets de manière cohérente sur la galerie et ailleurs dans le site.
Acceptance Criteria:
Given le composant ProjectCard est implémenté
When il reçoit les données d'un projet en props
Then il affiche l'image du projet (WebP, lazy loading)
And il affiche le titre traduit selon la langue courante
And il affiche la description courte traduite
And un hover effect révèle un CTA "Découvrir" avec animation subtile
And le composant est cliquable et navigue vers /projets/{slug} (ou /en/projects/{slug})
And le composant respecte prefers-reduced-motion pour les animations
And le composant est responsive (adaptation mobile/desktop)
And le composant est accessible (focus visible, role approprié)
Story 2.2 : Page Projets - Galerie
As a visiteur, I want voir la liste des projets réalisés par le développeur, So that je peux évaluer son expérience et choisir lesquels explorer en détail.
Acceptance Criteria:
Given le visiteur accède à /projets (FR) ou /en/projects (EN)
When la page se charge
Then une grille responsive de ProjectCard s'affiche
And les projets sont triés par date avec les "featured" en tête
And une animation d'entrée progressive des cards est présente (respectant prefers-reduced-motion)
And les données sont chargées depuis l'API /api/projects avec le contenu traduit
And les meta tags SEO sont dynamiques pour cette page
And le layout s'adapte : grille sur desktop, cards empilées sur mobile
Story 2.3 : Page Projet - Détail
As a visiteur, I want voir les détails d'un projet spécifique, So that je comprends le travail réalisé et les technologies utilisées.
Acceptance Criteria:
Given le visiteur accède à /projets/{slug} (FR) ou /en/projects/{slug} (EN)
When la page se charge
Then le titre, la description complète et l'image principale du projet s'affichent
And la date de réalisation est visible
And la liste des compétences utilisées s'affiche avec leurs niveaux (avant/après le projet)
And les liens externes sont présents : URL du projet live (si existe), repository GitHub (si existe)
And une navigation "Projet précédent / Projet suivant" permet de parcourir les projets
And un bouton retour vers la galerie est visible
And les meta tags SEO sont dynamiques (titre, description, image Open Graph)
And si le slug n'existe pas, une page 404 appropriée s'affiche
And le design est responsive (adaptation mobile/desktop)
Story 2.4 : Page Compétences - Affichage par catégories
As a visiteur, I want voir les compétences du développeur organisées par catégorie, So that je comprends son profil technique global.
Acceptance Criteria:
Given le visiteur accède à /competences (FR) ou /en/skills (EN)
When la page se charge
Then les compétences sont affichées groupées par catégorie (Frontend, Backend, Tools, Soft skills)
And chaque compétence affiche : icône, nom traduit, niveau actuel (représentation visuelle)
And les données sont chargées depuis l'API /api/skills avec le contenu traduit
And une animation d'entrée des éléments est présente (respectant prefers-reduced-motion)
And sur desktop : préparé pour accueillir le skill tree vis.js (Epic 3)
And sur mobile : liste groupée par catégorie avec design adapté
And les meta tags SEO sont dynamiques pour cette page
And chaque compétence est visuellement cliquable (affordance)
Story 2.5 : Compétences cliquables → Projets liés
As a visiteur, I want cliquer sur une compétence pour voir les projets qui l'utilisent, So that je peux voir des preuves concrètes de maîtrise.
Acceptance Criteria:
Given le visiteur est sur la page Compétences
When il clique sur une compétence
Then un panneau/modal s'ouvre avec la liste des projets liés à cette compétence
And pour chaque projet lié : titre, description courte, lien vers le détail
And l'indication du niveau avant/après chaque projet est visible (progression)
And une animation d'ouverture/fermeture fluide est présente (respectant prefers-reduced-motion)
And la fermeture est possible par clic extérieur, bouton close, ou touche Escape
And le panneau/modal utilise Headless UI pour l'accessibilité
And la navigation au clavier est fonctionnelle (Tab, Escape, Enter)
And le focus est piégé dans le modal quand ouvert (focus trap)
And les données viennent de la relation skill_project via l'API
Story 2.6 : Page Témoignages et migrations BDD
As a visiteur, I want voir les témoignages des personnes ayant travaillé avec le développeur, So that j'ai une validation sociale de ses compétences.
Acceptance Criteria:
Given les migrations Laravel sont exécutées
When php artisan migrate est lancé
Then la table testimonials est créée (id, name, role, company, avatar, text_key, personality ENUM, project_id FK nullable, display_order, is_active, timestamps)
And les seeders de test sont disponibles avec des témoignages en FR et EN
Given le visiteur accède à /temoignages (FR) ou /en/testimonials (EN)
When la page se charge
Then la liste des témoignages s'affiche depuis l'API /api/testimonials
And chaque témoignage affiche : nom, rôle, entreprise, avatar, texte traduit
And la personnalité de chaque PNJ est indiquée visuellement (style différent selon personality)
And un lien vers le projet associé est présent si pertinent
And l'ordre d'affichage respecte display_order
And le design est préparé pour accueillir le composant DialoguePNJ (story suivante)
And les meta tags SEO sont dynamiques pour cette page
Story 2.7 : Composant Dialogue PNJ
As a visiteur, I want lire les témoignages comme des dialogues de personnages style Zelda, So that l'expérience est immersive et mémorable.
Acceptance Criteria:
Given le composant DialoguePNJ est implémenté
When il reçoit les données d'un témoignage en props
Then l'avatar du PNJ s'affiche à gauche avec un style illustratif
And une bulle de dialogue s'affiche à droite avec le texte
And l'effet typewriter fait apparaître le texte lettre par lettre
And un clic ou appui sur Espace accélère l'animation typewriter (x3-x5)
And la personnalité du PNJ influence le style visuel de la bulle (sage, sarcastique, enthousiaste, professionnel)
And la police serif narrative est utilisée pour le texte du dialogue
And prefers-reduced-motion affiche le texte complet instantanément
And le texte complet est accessible via aria-label pour les screen readers
And une navigation entre témoignages est disponible (précédent/suivant)
And une transition animée s'effectue entre les PNJ
And un indicateur du témoignage actuel est visible (ex: 2/5)
And la navigation au clavier est fonctionnelle (flèches gauche/droite)
Story 2.8 : Page Parcours - Timeline narrative
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:
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
And chaque étape affiche : date, titre, description narrative traduite
And sur desktop : les étapes alternent gauche/droite pour un effet visuel dynamique
And sur mobile : les étapes sont linéaires (toutes du même côté)
And une animation d'apparition au scroll est présente (respectant prefers-reduced-motion)
And des icônes ou images illustrent les étapes clés
And le contenu est bilingue (FR/EN) et chargé depuis l'API ou fichiers i18n
And les meta tags SEO sont dynamiques pour cette page
And la police serif narrative est utilisée pour les descriptions
Epic 3 : Navigation Gamifiée & Progression
Le visiteur navigue via la carte interactive (Konva.js desktop / Chemin Libre mobile), est accompagné par le narrateur-guide (Le Bug), et voit sa progression sauvegardée automatiquement avec une barre XP.
Story 3.1 : Table narrator_texts et API narrateur
As a développeur, I want une infrastructure pour stocker et servir les textes du narrateur, So that le narrateur peut afficher des messages contextuels variés.
Acceptance Criteria:
Given les migrations Laravel sont exécutées
When php artisan migrate est lancé
Then la table narrator_texts est créée (id, context, text_key, variant, timestamps)
And les contextes définis incluent : intro, transition_projects, transition_skills, transition_testimonials, transition_journey, hint, encouragement_25, encouragement_50, encouragement_75, contact_unlocked, welcome_back
And plusieurs variantes par contexte permettent une sélection aléatoire
And les seeders insèrent les textes de base en FR et EN dans la table translations
Given l'API /api/narrator/{context} est appelée
When un contexte valide est fourni
Then un texte aléatoire parmi les variantes de ce contexte est retourné
And le texte est traduit selon le header Accept-Language
And le ton est adapté au héros (vouvoiement pour Recruteur, tutoiement pour Client/Dev)
Story 3.2 : Composant NarratorBubble (Le Bug)
As a visiteur, I want voir un narrateur-guide qui m'accompagne dans mon exploration, So that je me sens guidé et l'expérience est immersive.
Acceptance Criteria:
Given le composant NarratorBubble est implémenté
When le narrateur doit afficher un message
Then une bulle apparaît en bas de l'écran (desktop) ou au-dessus de la bottom bar (mobile)
And l'avatar du Bug (araignée) s'affiche avec son apparence selon le narratorStage du store
And le texte apparaît avec effet typewriter (lettre par lettre)
And un clic ou Espace accélère l'animation typewriter
And la bulle peut être fermée/minimisée sans bloquer la navigation
And le composant utilise aria-live="polite" et role="status" pour l'accessibilité
And prefers-reduced-motion affiche le texte instantanément
And la police serif narrative est utilisée pour le texte
And l'animation d'apparition/disparition est fluide et non-bloquante
Story 3.3 : Textes narrateur contextuels et arc de révélation
As a visiteur, I want que le narrateur réagisse à mes actions et évolue visuellement, So that l'expérience est personnalisée et le narrateur devient familier.
Acceptance Criteria:
Given le visiteur navigue sur le site When il effectue des actions clés Then le narrateur affiche un message d'accueil à l'arrivée (adapté au héros choisi) And des messages de transition s'affichent entre les zones And des encouragements apparaissent à 25%, 50%, 75% de progression And des indices s'affichent si le visiteur semble inactif (> 30s sans action) And un message spécial "Bienvenue à nouveau" s'affiche si progression existante détectée And le message de déblocage du contact s'affiche après 2 zones visitées
Given le visiteur progresse dans l'exploration
When le completionPercent atteint certains seuils
Then le narratorStage du store est mis à jour (1→5)
And l'apparence du Bug évolue : silhouette sombre (1) → forme vague (2) → pattes visibles (3) → araignée reconnaissable (4) → mascotte complète révélée (5)
And le ton du narrateur évolue de mystérieux à complice
Story 3.4 : Barre de progression globale (XP bar)
As a visiteur, I want voir ma progression dans l'exploration du site, So that je sais combien il me reste à découvrir.
Acceptance Criteria:
Given le visiteur est en mode Aventure
When il navigue sur le site
Then une barre de progression discrète s'affiche dans le header
And le pourcentage est calculé selon les sections visitées (Projets, Compétences, Témoignages, Parcours)
And l'animation de la barre est fluide lors des mises à jour
And un tooltip au hover indique les sections visitées et restantes
And le design évoque une barre XP style RPG (cohérent avec sky-accent)
And la barre respecte prefers-reduced-motion (pas d'animation si activé)
And sur mobile, la progression est accessible via la bottom bar
And la barre n'est pas visible en mode Express/Résumé
Story 3.5 : Logique de progression et déblocage contact
As a visiteur, I want que ma progression débloque l'accès au contact, So that l'exploration est récompensée sans être frustrante.
Acceptance Criteria:
Given le store useProgressionStore est actif
When le visiteur visite une nouvelle zone
Then la zone est ajoutée à visitedSections
And le completionPercent est recalculé automatiquement
And la progression est persistée en LocalStorage (si consentement RGPD donné)
Given le visiteur a visité 2 zones ou plus
When la condition est atteinte
Then contactUnlocked passe à true
And le narrateur annonce le déblocage avec un message spécial
And la zone Contact s'illumine sur la carte (si visible)
And le visiteur peut continuer à explorer ou aller au contact
Given le visiteur revient sur le site When une progression existe en LocalStorage Then le store est réhydraté avec l'état sauvegardé And le narrateur affiche "Bienvenue à nouveau" And la carte affiche l'état correct des zones visitées
Story 3.6 : Carte interactive desktop (Konva.js)
As a visiteur desktop, I want naviguer via une carte interactive visuelle, So that j'explore librement le portfolio comme un monde.
Acceptance Criteria:
Given le visiteur est sur desktop (≥ 1024px) et accède à la carte
When la carte se charge
Then un canvas Konva.js affiche une carte stylisée avec les zones (Projets, Compétences, Parcours, Témoignages, Contact)
And le composant est chargé en lazy-loading (.client.vue) pour respecter le budget JS
And chaque zone a une apparence distincte (teinte unique, icône)
And les zones visitées ont une apparence différente des zones non visitées
And la zone Contact est verrouillée visuellement si contactUnlocked est false
And la position actuelle du visiteur est marquée sur la carte
And au hover sur une zone : le nom et le statut s'affichent (tooltip)
And au clic sur une zone : navigation vers la section correspondante avec transition
And un curseur personnalisé indique les zones cliquables
And la navigation au clavier est fonctionnelle (Tab entre zones, Enter pour naviguer)
And les zones ont des labels ARIA descriptifs
Story 3.7 : Navigation mobile - Chemin Libre et Bottom Bar
As a visiteur mobile, I want naviguer facilement avec une interface adaptée au tactile, So that l'expérience reste immersive sur petit écran.
Acceptance Criteria:
Given le visiteur est sur mobile (< 768px)
When il accède à la navigation
Then le "Chemin Libre" affiche les zones en cards verticales scrollables (ZoneCard)
And chaque ZoneCard affiche : illustration, nom de la zone, statut (visité/nouveau/verrouillé)
And une ligne décorative relie les cards visuellement (effet chemin)
And un tap sur une zone navigue vers la section correspondante
And la zone Contact affiche un cadenas si contactUnlocked est false
Given la bottom bar mobile est affichée When le visiteur interagit Then 3 icônes sont accessibles : Carte (ouvre le Chemin Libre), Progression (affiche le %), Paramètres And les touch targets font au minimum 48x48px And la bottom bar est fixe et toujours visible And le narrateur s'affiche au-dessus de la bottom bar quand actif
Epic 4 : Chemins Narratifs, Challenge & Contact
Le visiteur fait des choix qui créent son parcours unique, relève un challenge optionnel, et accède à la révélation finale "Monde de Code" + formulaire de contact comme récompense narrative. Les easter eggs récompensent l'exploration.
Story 4.1 : Composant ChoiceCards et choix narratifs
As a visiteur, I want faire des choix qui influencent mon parcours, So that mon expérience est unique et personnalisée.
Acceptance Criteria:
Given le composant ChoiceCards est implémenté
When le narrateur propose un choix
Then 2 cards s'affichent côte à côte (desktop) ou empilées (mobile)
And chaque card affiche : icône, texte narratif du choix
And un hover/focus highlight la card sélectionnable
And un clic enregistre le choix dans choices du store Pinia
And une transition animée mène vers la destination choisie
And le composant est accessible (role="radiogroup", navigation clavier, focus visible)
And prefers-reduced-motion simplifie les animations
And le style est cohérent avec l'univers narratif (police serif, couleurs des zones)
Story 4.2 : Intro narrative et premier choix
As a visiteur aventurier, I want une introduction narrative captivante suivie d'un premier choix, So that je suis immergé dès le début de l'aventure.
Acceptance Criteria:
Given le visiteur a sélectionné son héros sur la landing page
When il commence l'aventure
Then une séquence d'intro narrative s'affiche avec le narrateur (Le Bug)
And le texte présente le "héros mystérieux" (le développeur) à découvrir
And l'effet typewriter anime le texte (skippable par clic/Espace)
And l'ambiance visuelle est immersive (fond sombre, illustrations)
And un bouton "Continuer" permet d'avancer
And à la fin de l'intro, le premier choix binaire s'affiche via ChoiceCards
And le choix propose deux zones à explorer en premier (ex: Projets vs Compétences)
And le contenu est bilingue (FR/EN) et adapté au héros (vouvoiement/tutoiement)
And la durée de l'intro est courte (15-30s max, skippable)
Story 4.3 : Chemins narratifs différenciés
As a visiteur, I want que mes choix aient un impact visible sur mon parcours, So that je sens que mon expérience est vraiment personnalisée.
Acceptance Criteria:
Given le visiteur fait des choix tout au long de l'aventure
When il navigue entre les zones
Then 2-3 points de choix binaires créent 4-8 parcours possibles
And chaque choix est enregistré dans choices du store
And l'ordre suggéré des zones varie selon le chemin choisi
And les textes du narrateur s'adaptent au chemin (transitions contextuelles)
And tous les chemins permettent de visiter tout le contenu
And tous les chemins mènent au contact (pas de "mauvais" choix)
And le currentPath du store reflète le chemin actuel
And à la fin de chaque zone, le narrateur propose un choix vers la suite
Story 4.4 : Table easter_eggs et système de détection
As a développeur, I want une infrastructure pour gérer les easter eggs cachés, So that je peux ajouter des surprises récompensant l'exploration.
Acceptance Criteria:
Given les migrations Laravel sont exécutées
When php artisan migrate est lancé
Then la table easter_eggs est créée (id, slug, location, trigger_type ENUM, reward_type ENUM, reward_key, difficulty, is_active, timestamps)
And les trigger_types incluent : click, hover, konami, scroll, sequence
And les reward_types incluent : snippet, anecdote, image, badge
And les seeders insèrent 5-10 easter eggs avec leurs récompenses traduites
Given l'API /api/easter-eggs est appelée
When la requête est faite
Then les métadonnées des easter eggs actifs sont retournées (slug, location, trigger_type)
And les réponses/récompenses ne sont PAS incluses (pour éviter la triche)
Given l'API /api/easter-eggs/{slug}/validate est appelée
When un slug valide est fourni
Then la récompense traduite est retournée
And l'easter egg est marqué comme trouvé côté client (store)
Story 4.5 : Easter eggs - Implémentation UI et collection
As a visiteur curieux, I want découvrir des surprises cachées et voir ma collection, So that l'exploration est récompensée.
Acceptance Criteria:
Given des easter eggs sont placés sur différentes pages
When le visiteur déclenche un easter egg (clic, hover, konami, scroll, sequence)
Then une animation de découverte s'affiche (popup, effet visuel)
And la récompense est affichée (snippet de code, anecdote, image, badge)
And le narrateur réagit avec enthousiasme
And une notification "Easter egg trouvé ! (X/Y)" s'affiche
And le slug est ajouté à easterEggsFound dans le store
And un bouton permet de fermer et continuer
Given le visiteur accède à sa collection (via paramètres ou zone dédiée) When la collection s'affiche Then une grille montre les easter eggs trouvés et des silhouettes mystère pour les non-trouvés And les détails sont visibles pour les découverts And un compteur X/Y indique la progression And un badge spécial s'affiche si 100% trouvés
Story 4.6 : Page Challenge - Structure et puzzle
As a visiteur, I want relever un défi optionnel avant d'accéder au contact, So that l'accès au développeur est une récompense méritée (mais pas bloquante).
Acceptance Criteria:
Given le visiteur accède à /challenge (après avoir débloqué le contact)
When la page se charge
Then une introduction narrative "Une dernière épreuve..." s'affiche
And un puzzle logique/code simple est présenté (réordonner, compléter, décoder)
And la difficulté est calibrée : 1-3 minutes pour résoudre
And le thème est lié au développement/code
And un système d'indices est disponible (bouton "Besoin d'aide ?")
And 3 niveaux d'indices progressifs sont proposés
And après 3 indices, une option "Passer" apparaît
And le challenge est TOUJOURS skippable (bouton discret "Passer directement au contact")
And une validation avec feedback clair indique succès/échec
And une animation de succès célèbre la réussite
And challengeCompleted est mis à true dans le store si réussi
Story 4.7 : Révélation "Monde de Code"
As a visiteur ayant complété le parcours, I want vivre un moment waouh de révélation finale, So that la découverte du développeur est mémorable.
Acceptance Criteria:
Given le visiteur accède à la zone Contact (après challenge ou skip)
When la révélation se déclenche
Then une transition immersive mène vers le "Monde de Code"
And un paysage composé de blocs de code ASCII art s'affiche (montagnes, arbres, ville en code)
And le code scroll/apparaît progressivement (animation)
And l'avatar illustré de Célian est révélé au centre du monde de code
And le narrateur (Le Bug) commente : "Tu l'as trouvé !"
And le message "Tu m'as trouvé !" s'affiche avec effet de célébration
And sur mobile, une version allégée mais émotionnellement équivalente s'affiche
And prefers-reduced-motion affiche une version statique
And une description alternative est disponible pour les screen readers
And un bouton permet de continuer vers le formulaire de contact
Story 4.8 : Page Contact - Formulaire et célébration
As a visiteur ayant trouvé le développeur, I want le contacter facilement avec une célébration, So that l'envoi du message est une conclusion satisfaisante.
Acceptance Criteria:
Given le visiteur est sur la page Contact après la révélation
When la page s'affiche
Then un message de félicitations avec stats du parcours est visible (zones visitées, easter eggs trouvés, temps passé)
And un formulaire de contact s'affiche : nom (requis), email (requis), message (requis)
And la validation temps réel est effectuée côté frontend (champs requis, format email)
And les erreurs sont communiquées par le narrateur (pas de messages d'erreur classiques)
And un champ honeypot invisible est présent (anti-spam)
And reCAPTCHA v3 est intégré de manière invisible
And le bouton d'envoi utilise la couleur accent (sky-accent)
Given le formulaire est soumis When les données sont envoyées à l'API Then la validation backend Laravel (Form Request) vérifie les données And le rate limiting (5 req/min par IP) est appliqué And l'email est envoyé via Laravel Mail And une animation de célébration s'affiche (confettis ou similaire) And le narrateur confirme l'envoi avec un message personnalisé And en cas d'erreur, le narrateur explique le problème avec bienveillance
Story 4.9 : Challenge post-formulaire
As a visiteur ayant envoyé un message, I want m'amuser en attendant la réponse, So that le temps d'attente est transformé en moment de jeu.
Acceptance Criteria:
Given le formulaire de contact a été envoyé avec succès When la confirmation s'affiche Then un message "En attendant que le développeur retrouve le chemin vers sa boîte mail..." est affiché And un challenge optionnel bonus est proposé And le challenge est différent du challenge principal (mini-jeu, quiz, exploration) And le visiteur peut fermer et quitter à tout moment And la participation est totalement optionnelle And le résultat n'impacte rien (juste pour le fun) And le narrateur commente avec humour