feat: implement CS2-inspired design system and UI components

This commit delivers a comprehensive design system and component library
inspired by Counter-Strike 2's tactical aesthetic.

Design System:
- Created docs/DESIGN.md with complete design language documentation
- CS2-inspired color palette: T-side orange (#d4a74a), CT-side blue (#5e98d9)
- Dark-first approach with tactical, data-dense layouts
- Typography scale, spacing system, and animation guidelines

Component Library:
- Button component: 4 variants (primary, secondary, ghost, danger), 3 sizes
- Badge component: 7 variants including team-specific badges
- Card component: 3 variants (default, elevated, interactive)
- Header component: Responsive navigation with mobile menu
- Footer component: Site-wide footer with organized link sections

Pages:
- Redesigned homepage with hero section, featured matches, features grid, CTA
- Created placeholder pages: /matches, /players, /about
- All pages follow CS2 aesthetic with proper component usage

Technical Fixes:
- Fixed Svelte 5 snippet syntax errors (removed incorrect render prop pattern)
- Fixed Card component accessibility (conditional button/div rendering)
- Removed invalid CSS border-border class from app.css
- Ensured zero TypeScript errors and warnings

Build Status: ✓ Verified with 0 errors, 0 warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-04 20:07:05 +01:00
parent 288438a953
commit 153c0e9f13
12 changed files with 1326 additions and 20 deletions

View File

@@ -0,0 +1,37 @@
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
variant?: 'default' | 't-side' | 'ct-side' | 'success' | 'warning' | 'error' | 'info';
size?: 'sm' | 'md' | 'lg';
class?: string;
children: Snippet;
}
let { variant = 'default', size = 'md', class: className = '', children }: Props = $props();
const baseClasses = 'inline-flex items-center justify-center font-medium border rounded';
const variantClasses = {
default: 'bg-base-300/50 border-base-content/20 text-base-content',
't-side':
'bg-terrorist/10 border-terrorist/30 text-terrorist-light backdrop-blur-sm font-semibold',
'ct-side': 'bg-ct/10 border-ct/30 text-ct-light backdrop-blur-sm font-semibold',
success: 'bg-success/10 border-success/30 text-success',
warning: 'bg-warning/10 border-warning/30 text-warning',
error: 'bg-error/10 border-error/30 text-error',
info: 'bg-info/10 border-info/30 text-info'
};
const sizeClasses = {
sm: 'px-2 py-0.5 text-xs',
md: 'px-2.5 py-1 text-sm',
lg: 'px-3 py-1.5 text-base'
};
const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`;
</script>
<span class={classes}>
{@render children()}
</span>

View File

@@ -0,0 +1,56 @@
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
href?: string;
type?: 'button' | 'submit' | 'reset';
disabled?: boolean;
class?: string;
onclick?: () => void;
children: Snippet;
}
let {
variant = 'primary',
size = 'md',
href,
type = 'button',
disabled = false,
class: className = '',
onclick,
children
}: Props = $props();
const baseClasses =
'inline-flex items-center justify-center font-semibold transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-base-100 disabled:opacity-50 disabled:cursor-not-allowed';
const variantClasses = {
primary:
'bg-primary text-white hover:bg-primary-focus focus:ring-primary shadow-sm hover:shadow-lg hover:shadow-primary/30',
secondary:
'bg-secondary text-white hover:bg-secondary-focus focus:ring-secondary shadow-sm hover:shadow-lg hover:shadow-secondary/30',
ghost:
'bg-transparent border border-base-300 text-base-content hover:bg-base-300 hover:border-primary focus:ring-primary',
danger: 'bg-error text-white hover:bg-error/90 focus:ring-error shadow-sm hover:shadow-lg'
};
const sizeClasses = {
sm: 'px-3 py-1.5 text-sm rounded',
md: 'px-4 py-2 text-base rounded-md',
lg: 'px-6 py-3 text-lg rounded-lg'
};
const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`;
</script>
{#if href}
<a {href} class={classes} aria-disabled={disabled}>
{@render children()}
</a>
{:else}
<button {type} {disabled} {onclick} class={classes}>
{@render children()}
</button>
{/if}

View File

@@ -0,0 +1,49 @@
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
variant?: 'default' | 'elevated' | 'interactive';
padding?: 'none' | 'sm' | 'md' | 'lg';
class?: string;
onclick?: () => void;
children: Snippet;
}
let {
variant = 'default',
padding = 'md',
class: className = '',
onclick,
children
}: Props = $props();
const baseClasses = 'bg-base-200 border border-base-300 rounded-md transition-all duration-200';
const variantClasses = {
default: 'shadow-sm',
elevated: 'shadow-lg shadow-black/10',
interactive:
'cursor-pointer hover:border-primary hover:shadow-lg hover:shadow-primary/20 hover:-translate-y-0.5'
};
const paddingClasses = {
none: '',
sm: 'p-3',
md: 'p-4',
lg: 'p-6'
};
const classes =
`${baseClasses} ${variantClasses[variant]} ${paddingClasses[padding]} ${className}` +
(onclick ? ' cursor-pointer' : '');
</script>
{#if onclick}
<button class={classes} {onclick}>
{@render children()}
</button>
{:else}
<div class={classes}>
{@render children()}
</div>
{/if}