Passe de polish visuel sur tout le site

Corrige deux incohérences trouvées en chemin : le compteur de passages dans le char en gold-bright sur marbre (même défaut de contraste déjà vu ailleurs), et le point du Crieur toujours rouge même pour un honneur positif. Réutilise les ornements existants mais jamais appelés (meander-divider entre le podium et le classement, DiamondDivider sur /profile avec une nouvelle convention de carte secondaire). Ajoute une ambiance discrète (fond qui respire très lentement, halo doré sur les cartes marbre, désactivés sous prefers-reduced-motion), étend les confettis et un nouveau son à Icare pour la parité avec la Corne d'Abondance, des loading.tsx sur les pages les plus consultées, et un petit ornement sur les états vides plutôt qu'un texte brut.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Valentin ROBIN
2026-08-26 15:03:52 +02:00
parent 187cdec0e2
commit 358bed0c2e
16 changed files with 245 additions and 73 deletions
+58
View File
@@ -0,0 +1,58 @@
"use client";
import { useState, type CSSProperties } from "react";
const CONFETTI_COLORS = ["#E7C560", "#C9A227", "#F4ECD8", "#A5342A", "#5E6B3B", "#2E6E7E", "#B08D57"];
type ConfettiPiece = {
id: number;
left: number;
color: string;
delay: number;
duration: number;
spin: number;
drift: number;
};
function generateConfettiPieces(): ConfettiPiece[] {
return Array.from({ length: 26 }, (_, index) => ({
id: index,
left: Math.random() * 100,
color: CONFETTI_COLORS[index % CONFETTI_COLORS.length],
delay: Math.random() * 250,
duration: 900 + Math.random() * 700,
spin: (Math.random() > 0.5 ? 1 : -1) * (360 + Math.random() * 360),
drift: (Math.random() - 0.5) * 70,
}));
}
// Partagé entre Le Vol d'Icare et La Corne d'Abondance (fin de partie) —
// à l'appelant de ne le monter que si prefers-reduced-motion ne le
// désactive pas, comme déjà fait sur les deux jeux.
export function Confetti() {
// Initialiseur paresseux de useState (n'exécute Math.random() qu'une
// seule fois, au montage — Confetti est remonté à chaque nouvelle partie
// terminée) plutôt qu'un appel impur dans le corps du composant.
const [pieces] = useState(generateConfettiPieces);
return (
<div className="pointer-events-none absolute inset-0 overflow-hidden">
{pieces.map((piece) => (
<span
key={piece.id}
className="animate-confetti-fall absolute top-0 h-2.5 w-1.5 rounded-[1px]"
style={
{
left: `${piece.left}%`,
backgroundColor: piece.color,
animationDelay: `${piece.delay}ms`,
animationDuration: `${piece.duration}ms`,
"--confetti-spin": `${piece.spin}deg`,
"--confetti-drift": `${piece.drift}px`,
} as CSSProperties
}
/>
))}
</div>
);
}