# Story 4.1: Composant ChoiceCards et choix narratifs Status: done ## Story As a visiteur, I want faire des choix qui influencent mon parcours, so that mon expérience est unique et personnalisée. ## Acceptance Criteria 1. **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) 2. **And** chaque card affiche : icône, texte narratif du choix 3. **And** un hover/focus highlight la card sélectionnable 4. **And** un clic enregistre le choix dans `choices` du store Pinia 5. **And** une transition animée mène vers la destination choisie 6. **And** le composant est accessible (`role="radiogroup"`, navigation clavier, focus visible) 7. **And** `prefers-reduced-motion` simplifie les animations 8. **And** le style est cohérent avec l'univers narratif (police serif, couleurs des zones) ## Tasks / Subtasks - [x] **Task 1: Définir les types de choix** (AC: #2, #4) - [x] Créer `frontend/app/types/choice.ts` - [x] Interface Choice : id, textFr, textEn, icon, destination, zoneColor - [x] Interface ChoicePoint : id, choices (2 options), context - [x] **Task 2: Créer le composant ChoiceCard** (AC: #2, #3, #8) - [x] Créer `frontend/app/components/feature/ChoiceCard.vue` - [x] Props : choice (Choice), selected (boolean), disabled (boolean) - [x] Afficher icône + texte narratif - [x] Effet hover/focus avec highlight - [x] Police serif narrative pour le texte - [x] **Task 3: Créer le composant ChoiceCards** (AC: #1, #4, #5, #6) - [x] Créer `frontend/app/components/feature/ChoiceCards.vue` - [x] Props : choicePoint (ChoicePoint) - [x] Emit : select (choice) - [x] Layout côte à côte desktop, empilé mobile - [x] Gérer la sélection et enregistrer dans le store - [x] Animation de transition vers la destination - [x] **Task 4: Implémenter l'accessibilité** (AC: #6) - [x] role="radiogroup" sur le conteneur - [x] role="radio" sur chaque card - [x] aria-checked pour indiquer la sélection - [x] Navigation clavier (flèches gauche/droite) - [x] Focus visible conforme WCAG - [x] **Task 5: Gérer les animations** (AC: #5, #7) - [x] Animation de sélection (scale + glow) - [x] Transition vers la destination (fade-out) - [x] Respecter prefers-reduced-motion - [x] **Task 6: Intégrer avec le store** (AC: #4) - [x] Appeler `progressionStore.makeChoice(id, value)` à la sélection - [x] Les choix sont persistés avec le reste de la progression - [x] **Task 7: Tests et validation** - [x] Build production réussi - [x] Validation TypeScript des composants ## Dev Notes ### Types des choix ```typescript // frontend/app/types/choice.ts export interface Choice { id: string textFr: string textEn: string icon: string // emoji ou URL d'image destination: string // route vers laquelle naviguer zoneColor: string // couleur de la zone associée } export interface ChoicePoint { id: string questionFr: string questionEn: string choices: [Choice, Choice] // Toujours 2 choix binaires context: string // contexte narratif (intro, after_projects, etc.) } // Exemple de point de choix export const CHOICE_POINTS: Record = { intro_first_choice: { id: 'intro_first_choice', questionFr: 'Par où veux-tu commencer ton exploration ?', questionEn: 'Where do you want to start your exploration?', choices: [ { id: 'choice_projects_first', textFr: 'Découvrir les créations', textEn: 'Discover the creations', icon: '💻', destination: '/projets', zoneColor: '#3b82f6', }, { id: 'choice_skills_first', textFr: 'Explorer les compétences', textEn: 'Explore the skills', icon: '⚡', destination: '/competences', zoneColor: '#10b981', }, ], context: 'intro', }, after_projects: { id: 'after_projects', questionFr: 'Quelle sera ta prochaine étape ?', questionEn: 'What will be your next step?', choices: [ { id: 'choice_testimonials', textFr: "Écouter ceux qui l'ont rencontré", textEn: 'Listen to those who met him', icon: '💬', destination: '/temoignages', zoneColor: '#f59e0b', }, { id: 'choice_journey', textFr: 'Suivre son parcours', textEn: 'Follow his journey', icon: '📍', destination: '/parcours', zoneColor: '#8b5cf6', }, ], context: 'after_projects', }, } ``` ### Composant ChoiceCard ```vue ``` ### Composant ChoiceCards ```vue ``` ### Utilisation dans une page/composant ```vue ``` ### Dépendances **Cette story nécessite :** - Story 3.5 : Store de progression (addChoice) - Story 3.2 : useReducedMotion composable **Cette story prépare pour :** - Story 4.2 : Intro narrative (utilise ChoiceCards) - Story 4.3 : Chemins narratifs (points de choix multiples) ### Project Structure Notes **Fichiers à créer :** ``` frontend/app/ ├── types/ │ └── choice.ts # CRÉER └── components/feature/ ├── ChoiceCard.vue # CRÉER └── ChoiceCards.vue # CRÉER ``` ### References - [Source: docs/planning-artifacts/epics.md#Story-4.1] - [Source: docs/planning-artifacts/ux-design-specification.md#Choice-System] - [Source: docs/brainstorming-gamification-2026-01-26.md#Parcours-Narratifs] ### Technical Requirements | Requirement | Value | Source | |-------------|-------|--------| | Choix par point | 2 (binaire) | Epics | | Layout desktop | Côte à côte | Epics | | Layout mobile | Empilé | Epics | | Accessibilité | role="radiogroup", clavier | Epics | | Police | font-narrative | UX Spec | ## Dev Agent Record ### Agent Model Used Claude Opus 4.5 (claude-opus-4-5-20251101) ### Debug Log References ### Completion Notes List - Composants ChoiceCard et ChoiceCards créés avec accessibilité complète - Types Choice et ChoicePoint définis avec CHOICE_POINTS prédéfinis - Intégration store via progressionStore.makeChoice() - Animations avec respect prefers-reduced-motion - Build production validé ### Change Log | Date | Change | Author | |------|--------|--------| | 2026-02-04 | Story créée avec contexte complet | SM Agent | | 2026-02-08 | Implémentation complète des composants | Dev Agent | ### File List - frontend/app/types/choice.ts (CREATED) - frontend/app/components/feature/ChoiceCard.vue (CREATED) - frontend/app/components/feature/ChoiceCards.vue (CREATED)