diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts
index dc023a2..8e8cf38 100644
--- a/apps/backend/src/app.ts
+++ b/apps/backend/src/app.ts
@@ -37,7 +37,22 @@ export async function buildApp() {
credentials: true,
});
- await app.register(helmet, { contentSecurityPolicy: false });
+ await app.register(helmet, {
+ contentSecurityPolicy: {
+ directives: {
+ defaultSrc: ["'self'"],
+ baseUri: ["'self'"],
+ fontSrc: ["'self'", 'https:', 'data:', 'https://fonts.gstatic.com'],
+ frameAncestors: ["'self'"],
+ imgSrc: ["'self'", 'data:', 'https:'],
+ objectSrc: ["'none'"],
+ scriptSrc: ["'self'"],
+ scriptSrcAttr: ["'none'"],
+ styleSrc: ["'self'", 'https:', "'unsafe-inline'", 'https://fonts.googleapis.com'],
+ upgradeInsecureRequests: [],
+ },
+ },
+ });
await app.register(jwt, {
secret: process.env.JWT_SECRET || 'dev-secret-change-me',
diff --git a/apps/web/src/app.css b/apps/web/src/app.css
index 772a760..bb09fef 100644
--- a/apps/web/src/app.css
+++ b/apps/web/src/app.css
@@ -1,47 +1,50 @@
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Outfit:wght@500;600;700;800;900&display=swap');
-/* light theme β default */
:root {
- /* Primary */
+ /* Primary Palette */
--primary: #6366f1;
- --primary-light: #818cf8;
- --primary-dark: #4f46e5;
- --accent: #8b5cf6;
-
- /* Background */
- --bg-primary: #f8fafc;
- --bg-secondary: #f1f5f9;
+ --primary-glow: rgba(99, 102, 241, 0.5);
+ --accent: #a855f7;
+ --accent-glow: rgba(168, 85, 247, 0.4);
+
+ /* Backgrounds */
+ --bg-primary: #ffffff;
+ --bg-secondary: #f8fafc;
+ --bg-glass: rgba(255, 255, 255, 0.7);
--bg-card: #ffffff;
- --bg-elevated: #e2e8f0;
-
+
/* Text */
--text-primary: #0f172a;
--text-secondary: #475569;
--text-muted: #94a3b8;
-
- /* Border */
- --border: #e2e8f0;
-
- /* Spacing */
+
+ /* Effects */
+ --border: rgba(226, 232, 240, 0.8);
+ --border-glass: rgba(255, 255, 255, 0.3);
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
+
--radius: 12px;
- --radius-lg: 16px;
- --radius-xl: 24px;
-
- --theme-transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
+ --radius-lg: 20px;
+ --radius-xl: 32px;
+
+ --theme-transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
-/* dark theme */
html.dark {
- --bg-primary: #0f0f1a;
- --bg-secondary: #1a1a2e;
- --bg-card: #16213e;
- --bg-elevated: #1e293b;
-
+ --bg-primary: #020617;
+ --bg-secondary: #0f172a;
+ --bg-glass: rgba(15, 23, 42, 0.6);
+ --bg-card: #0f172a;
+
--text-primary: #f8fafc;
- --text-secondary: #94a3b8;
+ --text-secondary: #cbd5e1;
--text-muted: #64748b;
-
- --border: #334155;
+
+ --border: rgba(30, 41, 59, 0.8);
+ --border-glass: rgba(255, 255, 255, 0.1);
}
* {
@@ -51,20 +54,53 @@ html.dark {
}
body {
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ font-family: 'Inter', sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
transition: var(--theme-transition);
-webkit-font-smoothing: antialiased;
min-height: 100vh;
+ overflow-x: hidden;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-family: 'Outfit', sans-serif;
+ font-weight: 700;
+ line-height: 1.1;
}
a {
- color: var(--primary);
+ color: inherit;
text-decoration: none;
- transition: color 0.2s;
+ transition: var(--theme-transition);
+}
+
+.glass {
+ background: var(--bg-glass);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border: 1px solid var(--border-glass);
+}
+
+.gradient-text {
+ background: linear-gradient(135deg, var(--primary), var(--accent));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, var(--primary), var(--accent));
+ color: white;
+ padding: 0.8rem 1.6rem;
+ border-radius: var(--radius);
+ font-weight: 600;
+ box-shadow: 0 4px 15px var(--primary-glow);
+ border: none;
+ cursor: pointer;
}
-a:hover {
- color: var(--primary-dark);
+.btn-primary:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px var(--primary-glow);
}
diff --git a/apps/web/src/hooks.server.ts b/apps/web/src/hooks.server.ts
new file mode 100644
index 0000000..e17520e
--- /dev/null
+++ b/apps/web/src/hooks.server.ts
@@ -0,0 +1,13 @@
+import type { Handle } from '@sveltejs/kit';
+
+export const handle: Handle = async ({ event, resolve }) => {
+ const response = await resolve(event);
+
+ // Security Headers (Note: CSP is handled in svelte.config.js)
+ response.headers.set('X-Content-Type-Options', 'nosniff');
+ response.headers.set('Referrer-Policy', 'no-referrer');
+ response.headers.set('X-Frame-Options', 'DENY');
+ response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
+
+ return response;
+};
diff --git a/apps/web/src/routes/+page.svelte b/apps/web/src/routes/+page.svelte
index 363a491..512f905 100644
--- a/apps/web/src/routes/+page.svelte
+++ b/apps/web/src/routes/+page.svelte
@@ -28,287 +28,218 @@
/>
+
+
+
+
+
β‘ DevCard
+
+ {theme === 'light' ? 'π' : 'βοΈ'}
+
+
+
+
-
- {theme === 'light' ? 'π' : 'βοΈ'}
-
- β‘
- DevCard
- One Tap. Every Profile. Every Platform.
+ GSSoC'26 Edition
+ One Tap. Every Profile. Every Platform.
- Stop sharing LinkedIn, GitHub, and Twitter one by one.
- DevCard puts every profile in one shareable QR code.
+ The open-source standard for developer networking. Put all your profilesβGitHub, LinkedIn, LeetCode, and moreβinto a single, high-impact digital card.
-
+
π
-
One Card, All Profiles
-
- GitHub, LinkedIn, Twitter/X, Devfolio, GitLab, LeetCode, and 10+ more β
- all in one card.
-
+
Unified Identity
+
Combine your fragmented online presence into a cohesive professional identity.
-
+
β‘
-
Hybrid Follow Engine
-
- Follow on GitHub silently via API. Connect on LinkedIn with one tap in
- WebView. No app switching.
-
-
-
-
π³
-
Context Cards
-
- Share your "Professional" card at conferences and "Hackathon" card at
- hack events. Same profiles, different contexts.
-
+
Instant Follow
+
Integrated APIs allow followers to connect with you instantly across platforms.
-
-
π±
-
QR + AirDrop
-
- Generate a QR code or share via AirDrop-style link. Works even if the
- receiver doesn't have the app.
-
-
-
+
π
-
Privacy First
-
- No data monetization. No tracking. Apache 2.0 licensed. You own your
- data.
-
-
-
-
π
-
Open Source
-
- Community-driven development. Contribute, self-host, or extend with your
- own platforms.
-
+
Private by Design
+
No tracking, no data selling. Your information stays where it belongs: with you.
diff --git a/apps/web/src/routes/u/[username]/+page.svelte b/apps/web/src/routes/u/[username]/+page.svelte
index f7750ec..d75e485 100644
--- a/apps/web/src/routes/u/[username]/+page.svelte
+++ b/apps/web/src/routes/u/[username]/+page.svelte
@@ -1,5 +1,6 @@
{#if profile}
- {profile.displayName} β DevCard
+ {profile.displayName} | DevCard
-
-
{:else}
- User Not Found β DevCard
+ User Not Found | DevCard
{/if}
-{#if error || !profile}
-
-
+
+
+
+ {#if error || !profile}
+
π
-
User Not Found
-
This DevCard doesn't exist or has been removed.
-
β Back to DevCard
+
Profile not found
+
This DevCard has vanished into the digital void.
+
Return Home
-
-{:else}
-
-
-
-