- Create HeroSection with animated search bar and stat counters - Add LiveMatchTicker with auto-scrolling recent matches - Build FlashLeaderboard "Wall of Shame" with podium display - Implement FeatureShowcase with scroll-triggered animations - Add NeonCTA call-to-action section with trust badges - Create reusable NeonButton component with glow effects Accessibility improvements: - Add aria-labels, aria-hidden for decorative elements - Implement focus-visible ring styles for keyboard navigation - Support prefers-reduced-motion across all animations - Use semantic HTML (article, nav, dl) for screen readers - Improve color contrast ratios for WCAG compliance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
125 lines
3.5 KiB
Svelte
125 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import { Trophy, Skull, Zap } from 'lucide-svelte';
|
|
|
|
interface Player {
|
|
rank: number;
|
|
name: string;
|
|
steamId: string;
|
|
avatarUrl?: string;
|
|
teammatesBlinded: number;
|
|
selfFlashes: number;
|
|
}
|
|
|
|
interface Props {
|
|
player: Player;
|
|
}
|
|
|
|
let { player }: Props = $props();
|
|
|
|
const podiumConfig = {
|
|
1: {
|
|
height: 'h-32',
|
|
bgGradient: 'from-yellow-500/20 to-yellow-600/5',
|
|
borderColor: 'border-yellow-500/50',
|
|
glowColor: 'shadow-yellow-500/30',
|
|
textColor: 'text-yellow-400',
|
|
title: 'Flash Criminal of the Week',
|
|
icon: Trophy
|
|
},
|
|
2: {
|
|
height: 'h-24',
|
|
bgGradient: 'from-gray-400/20 to-gray-500/5',
|
|
borderColor: 'border-gray-400/50',
|
|
glowColor: 'shadow-gray-400/30',
|
|
textColor: 'text-gray-300',
|
|
title: 'Serial Team Flasher',
|
|
icon: Skull
|
|
},
|
|
3: {
|
|
height: 'h-20',
|
|
bgGradient: 'from-amber-600/20 to-amber-700/5',
|
|
borderColor: 'border-amber-600/50',
|
|
glowColor: 'shadow-amber-600/30',
|
|
textColor: 'text-amber-500',
|
|
title: 'Flash Menace',
|
|
icon: Zap
|
|
}
|
|
};
|
|
|
|
const config = podiumConfig[player.rank as 1 | 2 | 3] || podiumConfig[3];
|
|
const IconComponent = config.icon;
|
|
|
|
// Shadow colors for each rank
|
|
const shadowColors: Record<number, string> = {
|
|
1: 'rgba(234, 179, 8, 0.2)',
|
|
2: 'rgba(156, 163, 175, 0.2)',
|
|
3: 'rgba(217, 119, 6, 0.2)'
|
|
};
|
|
const shadowColor = shadowColors[player.rank] || shadowColors[3];
|
|
</script>
|
|
|
|
<article
|
|
class="flex flex-col items-center"
|
|
aria-label="Rank {player.rank}: {player.name} - {player.teammatesBlinded} teammates blinded"
|
|
>
|
|
<!-- Player Card -->
|
|
<div
|
|
class="group relative mb-4 w-full max-w-[200px] overflow-hidden rounded-xl border bg-gradient-to-b p-4 transition-all hover:scale-105 motion-reduce:hover:scale-100 {config.borderColor} {config.bgGradient}"
|
|
style="box-shadow: 0 0 20px {shadowColor};"
|
|
>
|
|
<!-- Rank Badge -->
|
|
<div
|
|
class="absolute -right-2 -top-2 flex h-8 w-8 items-center justify-center rounded-full border-2 bg-void font-bold {config.borderColor} {config.textColor}"
|
|
aria-hidden="true"
|
|
>
|
|
#{player.rank}
|
|
</div>
|
|
|
|
<!-- Avatar -->
|
|
<div class="mx-auto mb-3 h-16 w-16 overflow-hidden rounded-full border-2 {config.borderColor}">
|
|
{#if player.avatarUrl}
|
|
<img
|
|
src={player.avatarUrl}
|
|
alt="Avatar for {player.name}"
|
|
class="h-full w-full object-cover"
|
|
/>
|
|
{:else}
|
|
<div
|
|
class="flex h-full w-full items-center justify-center bg-void-light"
|
|
aria-hidden="true"
|
|
>
|
|
<IconComponent class="h-8 w-8 {config.textColor}" />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<!-- Player Name -->
|
|
<h3 class="mb-1 truncate text-center font-semibold text-white">{player.name}</h3>
|
|
|
|
<!-- Title -->
|
|
<p class="mb-3 text-center text-xs {config.textColor}">{config.title}</p>
|
|
|
|
<!-- Stats -->
|
|
<dl class="space-y-1 text-center text-xs">
|
|
<div class="flex items-center justify-between gap-2">
|
|
<dt class="text-white/60">Teammates Blinded</dt>
|
|
<dd class="font-mono font-bold text-neon-red">{player.teammatesBlinded}</dd>
|
|
</div>
|
|
<div class="flex items-center justify-between gap-2">
|
|
<dt class="text-white/60">Self-Flashes</dt>
|
|
<dd class="font-mono font-bold text-white/80">{player.selfFlashes}</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
|
|
<!-- Podium Stand -->
|
|
<div
|
|
class="w-24 rounded-t-lg border-t-2 bg-gradient-to-b {config.height} {config.borderColor} {config.bgGradient}"
|
|
aria-hidden="true"
|
|
>
|
|
<div class="flex h-full items-center justify-center">
|
|
<span class="text-4xl font-bold {config.textColor}">{player.rank}</span>
|
|
</div>
|
|
</div>
|
|
</article>
|