Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added apps/web/check_output.txt
Binary file not shown.
7 changes: 7 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/kit": "^2.50.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tailwindcss/vite": "^4.3.0",
"autoprefixer": "^10.5.0",
"clsx": "^2.1.1",
"lucide-svelte": "^1.0.1",
"postcss": "^8.5.14",
"svelte": "^5.51.0",
"svelte-check": "^4.4.2",
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.3.0",
"typescript": "^5.9.3",
"vite": "^7.3.1"
}
Expand Down
188 changes: 131 additions & 57 deletions apps/web/src/app.css
Original file line number Diff line number Diff line change
@@ -1,70 +1,144 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');

/* light theme — default */
:root {
/* Primary */
--primary: #6366f1;
--primary-light: #818cf8;
--primary-dark: #4f46e5;
--accent: #8b5cf6;

/* Background */
--bg-primary: #f8fafc;
--bg-secondary: #f1f5f9;
--bg-card: #ffffff;
--bg-elevated: #e2e8f0;

/* Text */
--text-primary: #0f172a;
--text-secondary: #475569;
--text-muted: #94a3b8;

/* Border */
--border: #e2e8f0;

/* Spacing */
--radius: 12px;
--radius-lg: 16px;
--radius-xl: 24px;

--theme-transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
@import "tailwindcss";

@theme {
--color-primary: #7C3AED;
--color-secondary: #06B6D4;
--color-accent-pink: #EC4899;
--color-accent-blue: #3B82F6;
--color-dark-950: #030712;
--color-dark-900: #0B1020;

--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-display: "Space Grotesk", sans-serif;

--radius-xl: 1.5rem;
--radius-2xl: 2rem;
--radius-3xl: 3rem;
}

/* dark theme */
html.dark {
--bg-primary: #0f0f1a;
--bg-secondary: #1a1a2e;
--bg-card: #16213e;
--bg-elevated: #1e293b;
@layer base {
:root {
--bg-main: #f8fafc;
--bg-secondary: #ffffff;
--text-main: #0f172a;
--text-muted: #64748b;
--border-main: #e2e8f0;
--glass-bg: rgba(255, 255, 255, 0.7);
--glass-border: rgba(255, 255, 255, 0.5);
--primary-glow: rgba(124, 58, 237, 0.2);
}

--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--text-muted: #64748b;
.dark {
--bg-main: #030712;
--bg-secondary: #0B1020;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--border-main: #1f2937;
--glass-bg: rgba(3, 7, 18, 0.6);
--glass-border: rgba(255, 255, 255, 0.08);
--primary-glow: rgba(124, 58, 237, 0.3);
}

--border: #334155;
body {
@apply bg-(--bg-main) text-(--text-main) transition-colors duration-500 antialiased font-sans overflow-x-hidden;
}
}

* {
margin: 0;
padding: 0;
box-sizing: border-box;
@utility glass {
backdrop-filter: blur(20px) saturate(180%);
background-color: var(--glass-bg);
border: 1px solid var(--glass-border);
}

body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
transition: var(--theme-transition);
-webkit-font-smoothing: antialiased;
min-height: 100vh;
@utility glass-morphic {
@apply glass shadow-[0_8px_32px_0_rgba(31,38,135,0.07)];
}

a {
color: var(--primary);
text-decoration: none;
transition: color 0.2s;
@utility text-gradient {
@apply bg-clip-text text-transparent bg-linear-to-r from-primary via-secondary to-accent-pink;
}

a:hover {
color: var(--primary-dark);
@layer components {
.mesh-bg {
@apply absolute inset-0 -z-20 opacity-40;
background-image:
radial-gradient(at 0% 0%, rgba(124, 58, 237, 0.15) 0px, transparent 50%),
radial-gradient(at 100% 0%, rgba(6, 182, 212, 0.15) 0px, transparent 50%),
radial-gradient(at 100% 100%, rgba(236, 72, 153, 0.15) 0px, transparent 50%),
radial-gradient(at 0% 100%, rgba(59, 130, 246, 0.15) 0px, transparent 50%);
filter: blur(100px);
animation: mesh 20s ease infinite;
}

.card-cinematic {
@apply glass-morphic rounded-3xl p-8 transition-all duration-700 hover:-translate-y-2 hover:shadow-2xl hover:shadow-primary/20 relative overflow-hidden;
}

.btn-premium-primary {
@apply relative inline-flex items-center justify-center rounded-2xl font-black uppercase tracking-widest text-white transition-all duration-500 overflow-hidden;
background: linear-gradient(135deg, var(--color-primary), var(--color-accent-pink));
box-shadow: 0 4px 15px rgba(124, 58, 237, 0.4);
}
.btn-premium-primary:hover {
transform: translateY(-2px) scale(1.02);
box-shadow: 0 8px 25px rgba(124, 58, 237, 0.6);
}

.btn-premium-secondary {
@apply relative inline-flex items-center justify-center rounded-2xl font-black uppercase tracking-widest transition-all duration-500 overflow-hidden;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(124, 58, 237, 0.3);
color: var(--color-primary);
backdrop-filter: blur(10px);
}
.btn-premium-secondary:hover {
transform: translateY(-2px) scale(1.02);
background: rgba(124, 58, 237, 0.1);
border-color: rgba(124, 58, 237, 0.6);
}
}

@layer utilities {
.animate-mesh {
animation: mesh 20s ease infinite;
}

.animate-float {
animation: float 6s ease-in-out infinite;
}

.animate-pulse-glow {
animation: pulse-glow 4s ease-in-out infinite;
}
}

@keyframes mesh {
0%, 100% { background-position: 0% 50%; transform: scale(1); }
50% { background-position: 100% 50%; transform: scale(1.1); }
}

@keyframes float {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-20px) rotate(2deg); }
}

@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 20px rgba(124, 58, 237, 0.2); }
50% { box-shadow: 0 0 40px rgba(124, 58, 237, 0.4); }
}

/* Custom Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
@apply bg-transparent;
}
::-webkit-scrollbar-thumb {
@apply bg-primary/30 rounded-full hover:bg-primary/50 transition-colors;
}

/* Fluid Typography Helpers */
.text-fluid-h1 { font-size: clamp(2.5rem, 8vw, 6rem); }
.text-fluid-h2 { font-size: clamp(2rem, 5vw, 4rem); }
.text-fluid-p { font-size: clamp(1rem, 2vw, 1.25rem); }
7 changes: 6 additions & 1 deletion apps/web/src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
declare global {
namespace App {
// interface Error {}
// interface Locals {}
interface Locals {
user?: {
id: string;
username: string;
}
}
// interface PageData {}
// interface PageState {}
// interface Platform {}
Expand Down
26 changes: 26 additions & 0 deletions apps/web/src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
const token = event.cookies.get('token');

if (token) {
try {
// Call backend to verify token and get user info
const res = await event.fetch('http://localhost:3000/api/profiles/me');

if (res.ok) {
event.locals.user = await res.json();
} else {
event.cookies.delete('token', { path: '/' });
event.locals.user = undefined;
}
} catch (err) {
console.error('Auth verification failed:', err);
// Optional: fallback to mock for UI development if backend is not running
// event.locals.user = { id: 'mock-id', username: 'dev-user' };
}
}

const response = await resolve(event);
return response;
};
25 changes: 25 additions & 0 deletions apps/web/src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const BASE_URL = 'http://localhost:3000';

export async function fetchWithAuth(path: string, options: RequestInit = {}) {
// In SvelteKit, we should handle cookies properly
// For client-side calls, the browser sends the cookie automatically
const res = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});

if (!res.ok) {
throw new Error(`API Error: ${res.statusText}`);
}

return res.json();
}

export const api = {
getMe: () => fetchWithAuth('/api/profiles/me'),
getCards: () => fetchWithAuth('/api/cards'),
getProfiles: (username: string) => fetchWithAuth(`/api/u/${username}`),
};
28 changes: 28 additions & 0 deletions apps/web/src/lib/components/BrandIcon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
type IconName = 'github' | 'twitter' | 'linkedin' | 'discord' | 'google';

let { name, size = 24, class: className = '' }: {
name: IconName;
size?: number;
class?: string;
} = $props();

const paths: Record<IconName, string> = {
github: "M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.463-1.11-1.463-.908-.62.069-.069.069-.608.069 1.003.154 1.527 1.527 1.527 1.527.848 1.448 2.373 1.027 3.103.104.217.191.36.278.442-2.22-.251-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.115 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.202 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z",
twitter: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z",
linkedin: "M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z",
discord: "M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.196.373.291a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z",
google: "M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.747-.067-1.493-.187-2.093H12.48z"
};
</script>

<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="currentColor"
class={className}
>
<path d={paths[name]} />
</svg>
Loading