feat(frontend): composant NarratorBubble avec 5 stages du Bug

Story 3.2 : Implémentation du narrateur-guide "Le Bug"
- Composant NarratorBubble.vue avec effet typewriter
- 5 SVG représentant l'évolution de la mascotte (silhouette à révélation)
- Animation slide-up/fade-out avec prefers-reduced-motion
- Support clavier (Espace/Entrée pour skip, Échap pour fermer)
- Accessibilité (aria-live, role="status", sr-only)
- Responsive (position adaptée mobile avec bottom-bar)
- Traductions narrator.clickToSkip et narrator.bugAlt

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 02:54:16 +01:00
parent c572af3072
commit e882cd3e7a
10 changed files with 509 additions and 45 deletions

View File

@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" fill="none">
<!-- Stage 1: Silhouette sombre floue - mystérieuse forme indistincte -->
<defs>
<filter id="blur1" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="6"/>
</filter>
<radialGradient id="shadow1" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#1a1a2e"/>
<stop offset="70%" stop-color="#1a1a2e" stop-opacity="0.8"/>
<stop offset="100%" stop-color="#1a1a2e" stop-opacity="0"/>
</radialGradient>
</defs>
<!-- Forme mystérieuse floue -->
<ellipse cx="40" cy="42" rx="22" ry="20" fill="url(#shadow1)" filter="url(#blur1)"/>
<!-- Légère suggestion de présence -->
<ellipse cx="40" cy="40" rx="16" ry="14" fill="#16213e" opacity="0.7" filter="url(#blur1)"/>
</svg>

After

Width:  |  Height:  |  Size: 845 B

View File

@@ -0,0 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" fill="none">
<!-- Stage 2: Forme vague avec yeux - on commence à deviner une créature -->
<defs>
<filter id="blur2" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3"/>
</filter>
<radialGradient id="body2" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#2d3561"/>
<stop offset="100%" stop-color="#1a1a2e"/>
</radialGradient>
<radialGradient id="glow2" cx="50%" cy="30%" r="50%">
<stop offset="0%" stop-color="#00d9ff" stop-opacity="0.8"/>
<stop offset="100%" stop-color="#00d9ff" stop-opacity="0"/>
</radialGradient>
</defs>
<!-- Corps vague -->
<ellipse cx="40" cy="42" rx="20" ry="18" fill="url(#body2)" filter="url(#blur2)"/>
<!-- Yeux brillants qui percent l'obscurité -->
<ellipse cx="32" cy="36" rx="5" ry="4" fill="url(#glow2)"/>
<ellipse cx="48" cy="36" rx="5" ry="4" fill="url(#glow2)"/>
<!-- Pupilles -->
<circle cx="33" cy="36" r="2" fill="#0a0a14"/>
<circle cx="49" cy="36" r="2" fill="#0a0a14"/>
<!-- Reflets -->
<circle cx="31" cy="35" r="1" fill="#ffffff" opacity="0.6"/>
<circle cx="47" cy="35" r="1" fill="#ffffff" opacity="0.6"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,47 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" fill="none">
<!-- Stage 3: Pattes visibles - créature avec membres qui apparaissent -->
<defs>
<filter id="blur3" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="1.5"/>
</filter>
<radialGradient id="body3" cx="50%" cy="40%" r="60%">
<stop offset="0%" stop-color="#3f4c7d"/>
<stop offset="100%" stop-color="#1a1a2e"/>
</radialGradient>
<radialGradient id="glow3" cx="50%" cy="30%" r="50%">
<stop offset="0%" stop-color="#00d9ff"/>
<stop offset="100%" stop-color="#0099cc"/>
</radialGradient>
</defs>
<!-- Pattes (partiellement visibles) -->
<g stroke="#2d3561" stroke-width="3" stroke-linecap="round" filter="url(#blur3)">
<!-- Pattes gauches -->
<path d="M24 35 L12 28"/>
<path d="M22 42 L8 42"/>
<path d="M24 49 L14 58"/>
<!-- Pattes droites -->
<path d="M56 35 L68 28"/>
<path d="M58 42 L72 42"/>
<path d="M56 49 L66 58"/>
</g>
<!-- Corps plus défini -->
<ellipse cx="40" cy="42" rx="18" ry="16" fill="url(#body3)"/>
<!-- Tête -->
<circle cx="40" cy="32" r="10" fill="#3f4c7d"/>
<!-- Yeux -->
<ellipse cx="35" cy="31" rx="4" ry="3.5" fill="url(#glow3)"/>
<ellipse cx="45" cy="31" rx="4" ry="3.5" fill="url(#glow3)"/>
<!-- Pupilles -->
<circle cx="36" cy="31" r="1.5" fill="#0a0a14"/>
<circle cx="46" cy="31" r="1.5" fill="#0a0a14"/>
<!-- Reflets -->
<circle cx="34" cy="30" r="1" fill="#ffffff" opacity="0.7"/>
<circle cx="44" cy="30" r="1" fill="#ffffff" opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,65 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" fill="none">
<!-- Stage 4: Araignée reconnaissable - forme claire mais pas encore la mascotte finale -->
<defs>
<radialGradient id="body4" cx="50%" cy="35%" r="60%">
<stop offset="0%" stop-color="#5a6aad"/>
<stop offset="100%" stop-color="#2d3561"/>
</radialGradient>
<radialGradient id="glow4" cx="50%" cy="30%" r="50%">
<stop offset="0%" stop-color="#00ffff"/>
<stop offset="100%" stop-color="#00d9ff"/>
</radialGradient>
<filter id="glow4f" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="1"/>
</filter>
</defs>
<!-- Pattes bien définies -->
<g stroke="#3f4c7d" stroke-width="3" stroke-linecap="round">
<!-- Pattes gauches (4) -->
<path d="M26 30 L10 20 L6 12"/>
<path d="M24 38 L6 34 L2 28"/>
<path d="M24 46 L6 50 L2 56"/>
<path d="M26 52 L12 62 L8 70"/>
<!-- Pattes droites (4) -->
<path d="M54 30 L70 20 L74 12"/>
<path d="M56 38 L74 34 L78 28"/>
<path d="M56 46 L74 50 L78 56"/>
<path d="M54 52 L68 62 L72 70"/>
</g>
<!-- Abdomen -->
<ellipse cx="40" cy="48" rx="14" ry="12" fill="url(#body4)"/>
<!-- Céphalothorax -->
<ellipse cx="40" cy="34" rx="16" ry="14" fill="url(#body4)"/>
<!-- Motif sur l'abdomen -->
<path d="M40 38 L36 50 L40 56 L44 50 Z" fill="#4a5a9d" opacity="0.6"/>
<!-- Yeux (8 yeux d'araignée) -->
<g filter="url(#glow4f)">
<!-- Yeux principaux -->
<ellipse cx="35" cy="32" rx="4" ry="4" fill="url(#glow4)"/>
<ellipse cx="45" cy="32" rx="4" ry="4" fill="url(#glow4)"/>
<!-- Yeux secondaires -->
<circle cx="30" cy="28" r="2" fill="#00d9ff"/>
<circle cx="50" cy="28" r="2" fill="#00d9ff"/>
<circle cx="33" cy="38" r="1.5" fill="#00d9ff"/>
<circle cx="47" cy="38" r="1.5" fill="#00d9ff"/>
</g>
<!-- Pupilles principales -->
<circle cx="36" cy="32" r="2" fill="#0a0a14"/>
<circle cx="46" cy="32" r="2" fill="#0a0a14"/>
<!-- Reflets -->
<circle cx="34" cy="31" r="1.2" fill="#ffffff" opacity="0.8"/>
<circle cx="44" cy="31" r="1.2" fill="#ffffff" opacity="0.8"/>
<!-- Chélicères -->
<path d="M37 40 L35 44" stroke="#4a5a9d" stroke-width="2" stroke-linecap="round"/>
<path d="M43 40 L45 44" stroke="#4a5a9d" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,112 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" fill="none">
<!-- Stage 5: Mascotte complète révélée - Le Bug dans toute sa splendeur -->
<defs>
<radialGradient id="body5" cx="50%" cy="30%" r="70%">
<stop offset="0%" stop-color="#7b8cd4"/>
<stop offset="50%" stop-color="#5a6aad"/>
<stop offset="100%" stop-color="#3f4c7d"/>
</radialGradient>
<radialGradient id="glow5" cx="50%" cy="30%" r="50%">
<stop offset="0%" stop-color="#00ffff"/>
<stop offset="60%" stop-color="#00d9ff"/>
<stop offset="100%" stop-color="#0099cc"/>
</radialGradient>
<filter id="glowFilter5" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="2"/>
</filter>
<linearGradient id="legGrad5" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#5a6aad"/>
<stop offset="100%" stop-color="#3f4c7d"/>
</linearGradient>
</defs>
<!-- Halo de lumière derrière -->
<circle cx="40" cy="40" r="35" fill="#00d9ff" opacity="0.1" filter="url(#glowFilter5)"/>
<!-- Pattes avec détails -->
<g stroke="url(#legGrad5)" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round">
<!-- Pattes gauches -->
<path d="M26 28 L12 18 L8 8">
<animate attributeName="d" values="M26 28 L12 18 L8 8;M26 28 L11 17 L6 8;M26 28 L12 18 L8 8" dur="3s" repeatCount="indefinite"/>
</path>
<path d="M24 36 L8 32 L2 24"/>
<path d="M24 44 L8 48 L2 54"/>
<path d="M26 52 L14 62 L10 72">
<animate attributeName="d" values="M26 52 L14 62 L10 72;M26 52 L13 63 L8 72;M26 52 L14 62 L10 72" dur="3s" repeatCount="indefinite"/>
</path>
<!-- Pattes droites -->
<path d="M54 28 L68 18 L72 8">
<animate attributeName="d" values="M54 28 L68 18 L72 8;M54 28 L69 17 L74 8;M54 28 L68 18 L72 8" dur="3s" repeatCount="indefinite"/>
</path>
<path d="M56 36 L72 32 L78 24"/>
<path d="M56 44 L72 48 L78 54"/>
<path d="M54 52 L66 62 L70 72">
<animate attributeName="d" values="M54 52 L66 62 L70 72;M54 52 L67 63 L72 72;M54 52 L66 62 L70 72" dur="3s" repeatCount="indefinite"/>
</path>
</g>
<!-- Articulations des pattes -->
<g fill="#4a5a9d">
<circle cx="12" cy="18" r="2"/>
<circle cx="8" cy="32" r="2"/>
<circle cx="8" cy="48" r="2"/>
<circle cx="14" cy="62" r="2"/>
<circle cx="68" cy="18" r="2"/>
<circle cx="72" cy="32" r="2"/>
<circle cx="72" cy="48" r="2"/>
<circle cx="66" cy="62" r="2"/>
</g>
<!-- Abdomen avec motif -->
<ellipse cx="40" cy="50" rx="14" ry="12" fill="url(#body5)"/>
<path d="M40 40 L34 52 L40 60 L46 52 Z" fill="#8b9ce0" opacity="0.5"/>
<ellipse cx="40" cy="52" rx="3" ry="4" fill="#9daef0" opacity="0.3"/>
<!-- Céphalothorax -->
<ellipse cx="40" cy="34" rx="16" ry="14" fill="url(#body5)"/>
<!-- Visage amical -->
<!-- Yeux principaux expressifs -->
<g filter="url(#glowFilter5)">
<ellipse cx="34" cy="32" rx="6" ry="6" fill="url(#glow5)"/>
<ellipse cx="46" cy="32" rx="6" ry="6" fill="url(#glow5)"/>
</g>
<!-- Bordure des yeux -->
<ellipse cx="34" cy="32" rx="6" ry="6" stroke="#00ffff" stroke-width="0.5" fill="none"/>
<ellipse cx="46" cy="32" rx="6" ry="6" stroke="#00ffff" stroke-width="0.5" fill="none"/>
<!-- Pupilles avec personnalité -->
<ellipse cx="35" cy="33" rx="2.5" ry="3" fill="#0a0a14"/>
<ellipse cx="47" cy="33" rx="2.5" ry="3" fill="#0a0a14"/>
<!-- Reflets vifs -->
<circle cx="33" cy="31" r="1.8" fill="#ffffff"/>
<circle cx="45" cy="31" r="1.8" fill="#ffffff"/>
<circle cx="36" cy="34" r="0.8" fill="#ffffff" opacity="0.6"/>
<circle cx="48" cy="34" r="0.8" fill="#ffffff" opacity="0.6"/>
<!-- Yeux secondaires (plus petits, amicaux) -->
<circle cx="28" cy="27" r="2.5" fill="#00d9ff"/>
<circle cx="52" cy="27" r="2.5" fill="#00d9ff"/>
<circle cx="28" cy="27" r="1" fill="#0a0a14"/>
<circle cx="52" cy="27" r="1" fill="#0a0a14"/>
<!-- Petits yeux inférieurs -->
<circle cx="32" cy="40" r="1.5" fill="#00d9ff" opacity="0.8"/>
<circle cx="48" cy="40" r="1.5" fill="#00d9ff" opacity="0.8"/>
<!-- Sourire amical -->
<path d="M36 42 Q40 46 44 42" stroke="#4a5a9d" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<!-- Chélicères stylisées (petites, pas effrayantes) -->
<path d="M37 44 L36 47" stroke="#5a6aad" stroke-width="2" stroke-linecap="round"/>
<path d="M43 44 L44 47" stroke="#5a6aad" stroke-width="2" stroke-linecap="round"/>
<!-- Petites antennes/pédipalpes amicales -->
<path d="M32 24 L28 18" stroke="#5a6aad" stroke-width="2" stroke-linecap="round"/>
<path d="M48 24 L52 18" stroke="#5a6aad" stroke-width="2" stroke-linecap="round"/>
<circle cx="28" cy="18" r="2" fill="#7b8cd4"/>
<circle cx="52" cy="18" r="2" fill="#7b8cd4"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB