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
+3 -52
View File
@@ -43,11 +43,11 @@ import {
stepIcarus,
} from "@/lib/icarus/physics";
import type { Boost, Column, IcarusState, ShatterEffect } from "@/lib/icarus/types";
import { playBoostSound } from "@/lib/icarus/sound";
import { Confetti } from "@/components/confetti";
type Status = "idle" | "flying" | "dead";
const CONFETTI_COLORS = ["#E7C560", "#C9A227", "#F4ECD8", "#A5342A", "#5E6B3B", "#2E6E7E", "#B08D57"];
// 0 avant DIFFICULTY_START_SCORE, monte jusqu'à 1 sur DIFFICULTY_RAMP_SPAN
// points, puis reste à 1 (palier, pour ne pas devenir injouable).
function difficultyProgress(score: number): number {
@@ -499,6 +499,7 @@ export function IcarusGame({
const collectedIndex = boostsRef.current.findIndex((b) => collidesWithBoost(icarusRef.current.y, b));
if (collectedIndex !== -1) {
boostsRef.current.splice(collectedIndex, 1);
playBoostSound();
// Active le bouclier pour les N prochaines colonnes (distance
// aléatoire, voir plus bas) et déclenche l'animation de bond en
// avant. Icare se fige (vitesse verticale annulée, plus de
@@ -637,56 +638,6 @@ export function IcarusGame({
);
}
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,
}));
}
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 React.CSSProperties
}
/>
))}
</div>
);
}
function render(
ctx: CanvasRenderingContext2D,
canvas: HTMLCanvasElement,