# Story 2.1: Composant ProjectCard Status: ready-for-dev ## Story 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 1. **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) 2. **And** il affiche le titre traduit selon la langue courante 3. **And** il affiche la description courte traduite 4. **And** un hover effect révèle un CTA "Découvrir" avec animation subtile 5. **And** le composant est cliquable et navigue vers `/projets/{slug}` (ou `/en/projects/{slug}`) 6. **And** le composant respecte `prefers-reduced-motion` pour les animations 7. **And** le composant est responsive (adaptation mobile/desktop) 8. **And** le composant est accessible (focus visible, `role` approprié) ## Tasks / Subtasks - [ ] **Task 1: Créer le composant ProjectCard.vue** (AC: #1, #2, #3) - [ ] Créer le fichier `frontend/app/components/feature/ProjectCard.vue` - [ ] Définir les props TypeScript : `project` (object avec slug, image, title, shortDescription) - [ ] Utiliser `` pour l'image avec format WebP et lazy loading - [ ] Intégrer `useI18n()` pour le titre et la description traduits - [ ] Afficher titre (`project.title`) et description courte (`project.shortDescription`) - [ ] **Task 2: Implémenter le hover effect et CTA** (AC: #4) - [ ] Créer un overlay qui apparaît au hover avec transition CSS - [ ] Ajouter un CTA "Découvrir" (traduit via i18n) centré dans l'overlay - [ ] Animation subtile : fade-in + léger scale (0.98 → 1) - [ ] Utiliser les classes Tailwind pour les transitions - [ ] **Task 3: Implémenter la navigation** (AC: #5) - [ ] Rendre la card entièrement cliquable avec `` - [ ] Utiliser `localePath()` pour générer l'URL correcte selon la langue - [ ] URL pattern : `/projets/{slug}` (FR) ou `/en/projects/{slug}` (EN) - [ ] **Task 4: Gérer `prefers-reduced-motion`** (AC: #6) - [ ] Créer une media query CSS pour détecter `prefers-reduced-motion: reduce` - [ ] Désactiver les transitions et animations si motion réduite - [ ] Le hover effect reste visible mais sans animation - [ ] **Task 5: Rendre le composant responsive** (AC: #7) - [ ] Mobile : card pleine largeur, hauteur fixe ou aspect-ratio - [ ] Desktop : card avec largeur flexible pour grille (min 280px, max 400px) - [ ] Image qui remplit la card avec `object-cover` - [ ] Texte tronqué si trop long (ellipsis) - [ ] **Task 6: Accessibilité** (AC: #8) - [ ] Focus visible sur la card (outline accent) - [ ] `role="article"` sur la card container - [ ] `alt` descriptif sur l'image (utiliser le titre du projet) - [ ] Navigation au clavier fonctionnelle (Tab, Enter) - [ ] **Task 7: Tests et validation** - [ ] Tester le composant avec des données de projet fictives - [ ] Vérifier l'affichage en FR et EN - [ ] Vérifier le hover effect et la navigation - [ ] Tester sur mobile et desktop - [ ] Valider l'accessibilité avec axe DevTools ## Dev Notes ### Structure du composant ```vue ``` ### Interface TypeScript pour Project ```typescript // frontend/app/types/project.ts export interface Project { id: number slug: string image: string title: string // Déjà traduit par l'API description: string // Déjà traduit par l'API shortDescription: string // Déjà traduit par l'API url?: string githubUrl?: string dateCompleted: string isFeatured: boolean displayOrder: number skills?: ProjectSkill[] } export interface ProjectSkill { id: number slug: string name: string levelBefore: number levelAfter: number } ``` ### Clés i18n nécessaires Ajouter dans `frontend/i18n/fr.json` et `frontend/i18n/en.json` : ```json { "projects": { "discover": "Découvrir" } } ``` ```json { "projects": { "discover": "Discover" } } ``` ### Design Tokens utilisés | Token | Valeur | Usage | |-------|--------|-------| | `sky-dark` | Fond sombre | Overlay au hover | | `sky-accent` | #fa784f | CTA "Découvrir" | | `sky-text` | Blanc cassé | Titre projet | | `sky-text-muted` | Variante atténuée | Description courte | | `font-ui` | Inter | Tout le texte du composant | ### Comportement responsive | Breakpoint | Comportement | |------------|--------------| | Mobile (< 768px) | Card pleine largeur, hauteur image 180px | | Tablette (768px+) | Cards en grille 2 colonnes | | Desktop (1024px+) | Cards en grille 3-4 colonnes | ### Dépendances **Ce composant nécessite :** - Story 1.1 : Nuxt 4 initialisé avec `@nuxt/image`, `@nuxtjs/i18n`, TailwindCSS - Story 1.2 : Model Project avec structure de données - Story 1.3 : Système i18n configuré **Ce composant sera utilisé par :** - Story 2.2 : Page Projets - Galerie - Story 1.7 : Page Résumé Express (projets highlights) - Story 2.5 : Compétences cliquables (liste des projets liés) ### Project Structure Notes **Fichiers à créer :** ``` frontend/app/ ├── components/ │ └── feature/ │ └── ProjectCard.vue # CRÉER ├── types/ │ └── project.ts # CRÉER (si n'existe pas) ``` **Fichiers à modifier :** ``` frontend/i18n/ ├── fr.json # AJOUTER clés projects.* └── en.json # AJOUTER clés projects.* ``` ### References - [Source: docs/planning-artifacts/epics.md#Story-2.1] - [Source: docs/planning-artifacts/architecture.md#Frontend-Architecture] - [Source: docs/planning-artifacts/ux-design-specification.md#Visual-Design-Foundation] - [Source: docs/planning-artifacts/ux-design-specification.md#Accessibility-Strategy] ### Technical Requirements | Requirement | Value | Source | |-------------|-------|--------| | Image format | WebP avec fallback | NFR8 | | Lazy loading | Native via NuxtImg | NFR1, NFR2 | | Animations | Respect prefers-reduced-motion | NFR6 | | Accessibilité | WCAG AA | UX Spec | | Responsive | Mobile-first | UX Spec | ### Previous Story Intelligence (Epic 1) **Patterns établis à suivre :** - Composants feature dans `app/components/feature/` - Types TypeScript dans `app/types/` - Design tokens TailwindCSS : `sky-dark`, `sky-accent`, `sky-text` - Polices : `font-ui` (sans-serif), `font-narrative` (serif) - i18n via `useI18n()` et `localePath()` ## 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