# Story 3.6: Carte interactive desktop (Konva.js)
Status: ready-for-dev
## Story
As a visiteur desktop,
I want naviguer via une carte interactive visuelle,
so that j'explore librement le portfolio comme un monde.
## Acceptance Criteria
1. **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)
2. **And** le composant est chargé en lazy-loading (`.client.vue`) pour respecter le budget JS
3. **And** chaque zone a une apparence distincte (teinte unique, icône)
4. **And** les zones visitées ont une apparence différente des zones non visitées
5. **And** la zone Contact est verrouillée visuellement si `contactUnlocked` est `false`
6. **And** la position actuelle du visiteur est marquée sur la carte
7. **And** au hover sur une zone : le nom et le statut s'affichent (tooltip)
8. **And** au clic sur une zone : navigation vers la section correspondante avec transition
9. **And** un curseur personnalisé indique les zones cliquables
10. **And** la navigation au clavier est fonctionnelle (Tab entre zones, Enter pour naviguer)
11. **And** les zones ont des labels ARIA descriptifs
## Tasks / Subtasks
- [ ] **Task 1: Installer et configurer Konva.js** (AC: #2)
- [ ] Installer `konva` et `vue-konva`
- [ ] Configurer pour Nuxt (SSR-safe)
- [ ] Créer le wrapper `.client.vue` pour lazy-loading
- [ ] **Task 2: Définir la structure des zones** (AC: #1, #3)
- [ ] Créer les données des 5 zones : projets, competences, parcours, temoignages, contact
- [ ] Pour chaque zone : position (x, y), couleur, icône, label, route
- [ ] Design en forme d'île/territoire stylisé
- [ ] **Task 3: Créer le composant InteractiveMap** (AC: #1, #2)
- [ ] Créer `frontend/app/components/feature/InteractiveMap.client.vue`
- [ ] Initialiser le Stage et Layer Konva
- [ ] Dessiner le fond de carte (texture, grille, etc.)
- [ ] Placer les zones selon les positions définies
- [ ] **Task 4: Implémenter les états visuels des zones** (AC: #3, #4, #5)
- [ ] Zone non visitée : couleur atténuée, opacité réduite
- [ ] Zone visitée : couleur vive, checkmark ou brillance
- [ ] Zone Contact verrouillée : effet grisé + icône cadenas
- [ ] Zone Contact débloquée : brillance, invitation visuelle
- [ ] **Task 5: Implémenter le marqueur de position** (AC: #6)
- [ ] Créer un marqueur animé (pulsation)
- [ ] Positionner sur la zone actuelle (basé sur la route)
- [ ] Animer le déplacement entre zones
- [ ] **Task 6: Implémenter les interactions hover** (AC: #7, #9)
- [ ] Détecter le hover sur chaque zone
- [ ] Afficher un tooltip avec nom + statut
- [ ] Changer le curseur en pointer
- [ ] Effet de surbrillance sur la zone
- [ ] **Task 7: Implémenter les interactions clic** (AC: #8)
- [ ] Détecter le clic sur une zone
- [ ] Si zone accessible : naviguer avec router.push()
- [ ] Si zone Contact verrouillée : afficher message ou shake
- [ ] Animation de transition (zoom ou fade)
- [ ] **Task 8: Implémenter l'accessibilité** (AC: #10, #11)
- [ ] Rendre les zones focusables (tabindex)
- [ ] Gérer Tab pour naviguer entre zones
- [ ] Gérer Enter/Space pour cliquer
- [ ] Ajouter aria-label descriptif à chaque zone
- [ ] Ajouter role="button" aux zones cliquables
- [ ] **Task 9: Responsive et performance**
- [ ] Masquer la carte sous 1024px (afficher alternative mobile)
- [ ] Optimiser les redessins (cache les images)
- [ ] Lazy-load les images des zones
- [ ] **Task 10: Tests et validation**
- [ ] Tester le chargement lazy
- [ ] Vérifier les 5 zones distinctes
- [ ] Tester les états (visité/non visité/verrouillé)
- [ ] Valider hover et clic
- [ ] Tester navigation clavier
- [ ] Vérifier accessibilité (screen reader)
## Dev Notes
### Installation de Konva
```bash
# Dans le dossier frontend
pnpm add konva vue-konva
```
### Nuxt Config (Konva SSR-safe)
```typescript
// nuxt.config.ts
export default defineNuxtConfig({
// ...
build: {
transpile: ['konva', 'vue-konva'],
},
})
```
### Définition des zones
```typescript
// frontend/app/data/mapZones.ts
import type { Section } from '~/stores/progression'
export interface MapZone {
id: Section | 'contact'
label: {
fr: string
en: string
}
route: {
fr: string
en: string
}
position: { x: number; y: number }
color: string
icon: string // URL ou emoji
size: number // rayon ou taille
}
export const mapZones: MapZone[] = [
{
id: 'projets',
label: { fr: 'Projets', en: 'Projects' },
route: { fr: '/projets', en: '/en/projects' },
position: { x: 200, y: 150 },
color: '#3b82f6', // blue-500
icon: '/images/map/icon-projects.svg',
size: 80,
},
{
id: 'competences',
label: { fr: 'Compétences', en: 'Skills' },
route: { fr: '/competences', en: '/en/skills' },
position: { x: 450, y: 120 },
color: '#10b981', // emerald-500
icon: '/images/map/icon-skills.svg',
size: 80,
},
{
id: 'temoignages',
label: { fr: 'Témoignages', en: 'Testimonials' },
route: { fr: '/temoignages', en: '/en/testimonials' },
position: { x: 350, y: 280 },
color: '#f59e0b', // amber-500
icon: '/images/map/icon-testimonials.svg',
size: 80,
},
{
id: 'parcours',
label: { fr: 'Parcours', en: 'Journey' },
route: { fr: '/parcours', en: '/en/journey' },
position: { x: 550, y: 300 },
color: '#8b5cf6', // violet-500
icon: '/images/map/icon-journey.svg',
size: 80,
},
{
id: 'contact',
label: { fr: 'Contact', en: 'Contact' },
route: { fr: '/contact', en: '/en/contact' },
position: { x: 650, y: 180 },
color: '#fa784f', // sky-accent
icon: '/images/map/icon-contact.svg',
size: 80,
},
]
```
### Composant InteractiveMap
```vue
```
### Clés i18n
**fr.json :**
```json
{
"map": {
"ariaLabel": "Carte interactive du portfolio. Utilisez Tab pour naviguer entre les zones et Entrée pour explorer.",
"instructions": "Utilisez les touches Tab pour naviguer entre les zones et Entrée ou Espace pour explorer une zone.",
"locked": "Zone verrouillée - Explorez davantage pour débloquer",
"visited": "Déjà visité",
"clickToExplore": "Cliquez pour explorer",
"legend": {
"notVisited": "Non visité",
"visited": "Visité",
"locked": "Verrouillé"
}
}
}
```
**en.json :**
```json
{
"map": {
"ariaLabel": "Interactive portfolio map. Use Tab to navigate between zones and Enter to explore.",
"instructions": "Use Tab keys to navigate between zones and Enter or Space to explore a zone.",
"locked": "Locked zone - Explore more to unlock",
"visited": "Already visited",
"clickToExplore": "Click to explore",
"legend": {
"notVisited": "Not visited",
"visited": "Visited",
"locked": "Locked"
}
}
}
```
### Utilisation dans une page
```vue
```
### Dépendances
**Cette story nécessite :**
- Story 3.5 : Store de progression (visitedSections, contactUnlocked)
- Nuxt/Vue 3 avec support Konva
**Cette story prépare pour :**
- Story 3.7 : Navigation mobile (alternative à la carte)
- Story 4.2 : Intro narrative (peut utiliser la carte)
### Project Structure Notes
**Fichiers à créer :**
```
frontend/
├── app/
│ ├── components/feature/
│ │ └── InteractiveMap.client.vue # CRÉER
│ └── data/
│ └── mapZones.ts # CRÉER
└── public/images/map/
├── icon-projects.svg # CRÉER (optionnel)
├── icon-skills.svg # CRÉER (optionnel)
├── icon-testimonials.svg # CRÉER (optionnel)
├── icon-journey.svg # CRÉER (optionnel)
└── icon-contact.svg # CRÉER (optionnel)
```
**Fichiers à modifier :**
```
frontend/package.json # AJOUTER konva, vue-konva
frontend/nuxt.config.ts # AJOUTER transpile konva
frontend/i18n/fr.json # AJOUTER map.*
frontend/i18n/en.json # AJOUTER map.*
```
### References
- [Source: docs/planning-artifacts/epics.md#Story-3.6]
- [Source: docs/planning-artifacts/ux-design-specification.md#Interactive-Map]
- [Source: docs/planning-artifacts/architecture.md#JS-Budget]
- [Konva.js Documentation](https://konvajs.org/)
### Technical Requirements
| Requirement | Value | Source |
|-------------|-------|--------|
| Breakpoint desktop | >= 1024px | Epics |
| Bibliothèque canvas | Konva.js + vue-konva | Architecture |
| Chargement | Lazy (.client.vue) | JS Budget |
| Zones | 5 (projets, competences, temoignages, parcours, contact) | Epics |
| Accessibilité | Tab + Enter/Space, ARIA | Epics |
## 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 |
### File List