Files
csgowtf/src/lib/components/landing/LeaderboardPodium.svelte
vikingowl 1ddda81d93 feat: Add neon esports landing page with WCAG accessibility
- 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>
2025-12-07 16:10:13 +01:00

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>