Files
csgowtf/src/lib/components/landing/LiveMatchTicker.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

82 lines
2.5 KiB
Svelte

<script lang="ts">
import LiveMatchTickerCard from './LiveMatchTickerCard.svelte';
import { Activity } from 'lucide-svelte';
interface Match {
id: string;
map: string;
scoreT: number;
scoreCT: number;
isProcessing?: boolean;
timestamp?: string;
}
interface Props {
matches?: Match[];
}
// Sample matches for demo - in production, this would come from the API
let {
matches = [
{ id: '1', map: 'de_dust2', scoreT: 16, scoreCT: 14, isProcessing: true },
{ id: '2', map: 'de_mirage', scoreT: 13, scoreCT: 16 },
{ id: '3', map: 'de_inferno', scoreT: 16, scoreCT: 9 },
{ id: '4', map: 'de_ancient', scoreT: 11, scoreCT: 16 },
{ id: '5', map: 'de_anubis', scoreT: 16, scoreCT: 12, isProcessing: true },
{ id: '6', map: 'de_nuke', scoreT: 8, scoreCT: 16 },
{ id: '7', map: 'de_overpass', scoreT: 16, scoreCT: 14 },
{ id: '8', map: 'de_vertigo', scoreT: 14, scoreCT: 16 }
]
}: Props = $props();
// Duplicate matches for seamless loop with unique keys
const duplicatedMatches = $derived([
...matches.map((m, i) => ({ ...m, uniqueKey: `first-${i}-${m.id}` })),
...matches.map((m, i) => ({ ...m, uniqueKey: `second-${i}-${m.id}` }))
]);
</script>
<section class="relative overflow-hidden bg-void py-8" aria-labelledby="recent-matches-heading">
<!-- Section Header -->
<div class="container mx-auto mb-6 flex items-center justify-between px-4">
<div class="flex items-center gap-3">
<Activity
class="h-5 w-5 animate-pulse text-neon-green motion-reduce:animate-none"
aria-hidden="true"
/>
<h2 id="recent-matches-heading" class="text-lg font-semibold text-white">Recent Matches</h2>
</div>
<a
href="/matches"
class="rounded text-sm text-neon-blue transition-colors hover:text-neon-blue/80 focus:outline-none focus:ring-2 focus:ring-neon-blue focus:ring-offset-2 focus:ring-offset-void"
>
View all →
</a>
</div>
<!-- Ticker Container -->
<div class="relative">
<!-- Left Fade -->
<div
class="pointer-events-none absolute left-0 top-0 z-10 h-full w-24 bg-gradient-to-r from-void to-transparent"
aria-hidden="true"
></div>
<!-- Right Fade -->
<div
class="pointer-events-none absolute right-0 top-0 z-10 h-full w-24 bg-gradient-to-l from-void to-transparent"
aria-hidden="true"
></div>
<!-- Scrolling Ticker -->
<nav
class="hover:pause-animation flex animate-ticker gap-4 motion-reduce:animate-none"
aria-label="Recent match scores"
>
{#each duplicatedMatches as match (match.uniqueKey)}
<LiveMatchTickerCard {match} />
{/each}
</nav>
</div>
</section>